ReactOS  0.4.15-dev-4853-g3a72a52
section.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1998-2005 ReactOS Team (and the authors from the programmers section)
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  *
19  * PROJECT: ReactOS kernel
20  * FILE: ntoskrnl/mm/section.c
21  * PURPOSE: Implements section objects
22  *
23  * PROGRAMMERS: Rex Jolliff
24  * David Welch
25  * Eric Kohl
26  * Emanuele Aliberti
27  * Eugene Ingerman
28  * Casper Hornstrup
29  * KJK::Hyperion
30  * Guido de Jong
31  * Ge van Geldorp
32  * Royce Mitchell III
33  * Filip Navara
34  * Aleksey Bragin
35  * Jason Filby
36  * Thomas Weidenmueller
37  * Gunnar Andre' Dalsnes
38  * Mike Nordell
39  * Alex Ionescu
40  * Gregor Anich
41  * Steven Edwards
42  * Herve Poussineau
43  */
44 
45 /* INCLUDES *****************************************************************/
46 
47 #include <ntoskrnl.h>
48 #include <cache/newcc.h>
49 #include <cache/section/newmm.h>
50 #define NDEBUG
51 #include <debug.h>
52 #include <reactos/exeformat.h>
53 
54 #include "ARM3/miarm.h"
55 
56 #undef MmSetPageEntrySectionSegment
57 #define MmSetPageEntrySectionSegment(S,O,E) do { \
58  DPRINT("SetPageEntrySectionSegment(old,%p,%x,%x)\n",(S),(O)->LowPart,E); \
59  _MmSetPageEntrySectionSegment((S),(O),(E),__FILE__,__LINE__); \
60  } while (0)
61 
62 extern MMSESSION MmSession;
63 
64 static LARGE_INTEGER TinyTime = {{-1L, -1L}};
65 
66 #ifndef NEWCC
68 
69 VOID
70 NTAPI
72 {
73  //DPRINT("MmLockSectionSegment(%p,%s:%d)\n", Segment, file, line);
75  Segment->Locked = TRUE;
76 }
77 
78 VOID
79 NTAPI
81 {
82  ASSERT(Segment->Locked);
83  Segment->Locked = FALSE;
85  //DPRINT("MmUnlockSectionSegment(%p,%s:%d)\n", Segment, file, line);
86 }
87 #endif
88 
89 static
92 {
93  KIRQL OldIrql = MiAcquirePfnLock();
95 
96  while (TRUE)
97  {
98  Segment = SectionObjectPointer->DataSectionObject;
99  if (!Segment)
100  break;
101 
102  if (Segment->SegFlags & (MM_SEGMENT_INCREATE | MM_SEGMENT_INDELETE))
103  {
104  MiReleasePfnLock(OldIrql);
106  OldIrql = MiAcquirePfnLock();
107  continue;
108  }
109 
110  ASSERT(Segment->SegFlags & MM_DATAFILE_SEGMENT);
111  InterlockedIncrement64(&Segment->RefCount);
112  break;
113  }
114 
115  MiReleasePfnLock(OldIrql);
116 
117  return Segment;
118 }
119 
120 /* Somewhat grotesque, but eh... */
122 {
123  ASSERT((Segment->SegFlags & MM_DATAFILE_SEGMENT) == 0);
124 
125  return CONTAINING_RECORD(Segment->ReferenceCount, MM_IMAGE_SECTION_OBJECT, RefCount);
126 }
127 
128 NTSTATUS
130  IN PVOID Session,
134 
135 NTSTATUS
136 NTAPI
140  IN PLARGE_INTEGER InputMaximumSize,
145 
146 NTSTATUS
147 NTAPI
157  IN ULONG Protect);
158 
159 //
160 // PeFmtCreateSection depends on the following:
161 //
164 
167 C_ASSERT(FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader) == FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader));
168 
178 
179 /* TYPES *********************************************************************/
180 
181 typedef struct
182 {
190 }
192 
193 /* GLOBALS *******************************************************************/
194 
196 
198 
200 {
201  PAGE_NOACCESS, /* 0 = NONE */
202  PAGE_NOACCESS, /* 1 = SHARED */
203  PAGE_EXECUTE, /* 2 = EXECUTABLE */
204  PAGE_EXECUTE, /* 3 = EXECUTABLE, SHARED */
205  PAGE_READONLY, /* 4 = READABLE */
206  PAGE_READONLY, /* 5 = READABLE, SHARED */
207  PAGE_EXECUTE_READ, /* 6 = READABLE, EXECUTABLE */
208  PAGE_EXECUTE_READ, /* 7 = READABLE, EXECUTABLE, SHARED */
209  /*
210  * FIXME? do we really need the WriteCopy field in segments? can't we use
211  * PAGE_WRITECOPY here?
212  */
213  PAGE_READWRITE, /* 8 = WRITABLE */
214  PAGE_READWRITE, /* 9 = WRITABLE, SHARED */
215  PAGE_EXECUTE_READWRITE, /* 10 = WRITABLE, EXECUTABLE */
216  PAGE_EXECUTE_READWRITE, /* 11 = WRITABLE, EXECUTABLE, SHARED */
217  PAGE_READWRITE, /* 12 = WRITABLE, READABLE */
218  PAGE_READWRITE, /* 13 = WRITABLE, READABLE, SHARED */
219  PAGE_EXECUTE_READWRITE, /* 14 = WRITABLE, READABLE, EXECUTABLE */
220  PAGE_EXECUTE_READWRITE, /* 15 = WRITABLE, READABLE, EXECUTABLE, SHARED */
221 };
222 
223 extern ULONG MmMakeFileAccess [];
226 {
231 };
232 
233 
234 /* FUNCTIONS *****************************************************************/
235 
236 
237 
238 NTSTATUS
239 NTAPI
241  LONGLONG SegOffset,
243 /*
244  * FUNCTION: write a page for a section backed memory area.
245  * PARAMETERS:
246  * MemoryArea - Memory area to write the page for.
247  * Offset - Offset of the page to write.
248  * Page - Page which contains the data to write.
249  */
250 {
253  KEVENT Event;
254  UCHAR MdlBase[sizeof(MDL) + sizeof(PFN_NUMBER)];
255  PMDL Mdl = (PMDL)MdlBase;
256  PFILE_OBJECT FileObject = Segment->FileObject;
258 
259  FileOffset.QuadPart = Segment->Image.FileOffset + SegOffset;
260 
261  RtlZeroMemory(MdlBase, sizeof(MdlBase));
264  Mdl->MdlFlags |= MDL_PAGES_LOCKED;
265 
268  if (Status == STATUS_PENDING)
269  {
271  Status = IoStatus.Status;
272  }
273  if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
274  {
275  MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
276  }
277 
278  return Status;
279 }
280 
281 
282 /*
283  References:
284  [1] Microsoft Corporation, "Microsoft Portable Executable and Common Object
285  File Format Specification", revision 6.0 (February 1999)
286 */
288  IN SIZE_T FileHeaderSize,
289  IN PVOID File,
290  OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
291  OUT PULONG Flags,
292  IN PEXEFMT_CB_READ_FILE ReadFileCb,
293  IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb)
294 {
295  NTSTATUS nStatus;
296  ULONG cbFileHeaderOffsetSize = 0;
297  ULONG cbSectionHeadersOffset = 0;
298  ULONG cbSectionHeadersSize;
299  ULONG cbSectionHeadersOffsetSize = 0;
300  ULONG cbOptHeaderSize;
301  ULONG cbHeadersSize = 0;
302  ULONG nSectionAlignment;
303  ULONG nFileAlignment;
304  ULONG_PTR ImageBase = 0;
305  const IMAGE_DOS_HEADER * pidhDosHeader;
306  const IMAGE_NT_HEADERS32 * pinhNtHeader;
307  const IMAGE_OPTIONAL_HEADER32 * piohOptHeader;
308  const IMAGE_SECTION_HEADER * pishSectionHeaders;
309  PMM_SECTION_SEGMENT pssSegments;
310  LARGE_INTEGER lnOffset;
311  PVOID pBuffer;
312  SIZE_T nPrevVirtualEndOfSegment = 0;
313  ULONG nFileSizeOfHeaders = 0;
314  ULONG i;
315  ULONG AlignedLength;
316 
317  ASSERT(FileHeader);
318  ASSERT(FileHeaderSize > 0);
319  ASSERT(File);
320  ASSERT(ImageSectionObject);
321  ASSERT(ReadFileCb);
322  ASSERT(AllocateSegmentsCb);
323 
324  ASSERT(Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize));
325 
326  ASSERT(((UINT_PTR)FileHeader % TYPE_ALIGNMENT(IMAGE_DOS_HEADER)) == 0);
327 
328 #define DIE(ARGS_) { DPRINT ARGS_; goto l_Return; }
329 
330  pBuffer = NULL;
331  pidhDosHeader = FileHeader;
332 
333  /* DOS HEADER */
335 
336  /* image too small to be an MZ executable */
337  if(FileHeaderSize < sizeof(IMAGE_DOS_HEADER))
338  DIE(("Too small to be an MZ executable, size is %lu\n", FileHeaderSize));
339 
340  /* no MZ signature */
341  if(pidhDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
342  DIE(("No MZ signature found, e_magic is %hX\n", pidhDosHeader->e_magic));
343 
344  /* NT HEADER */
346 
347  /* not a Windows executable */
348  if(pidhDosHeader->e_lfanew <= 0)
349  DIE(("Not a Windows executable, e_lfanew is %d\n", pidhDosHeader->e_lfanew));
350 
351  if(!Intsafe_AddULong32(&cbFileHeaderOffsetSize, pidhDosHeader->e_lfanew, RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader)))
352  DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
353 
354  if(FileHeaderSize < cbFileHeaderOffsetSize)
355  pinhNtHeader = NULL;
356  else
357  {
358  /*
359  * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
360  * and FileHeaderSize >= cbFileHeaderOffsetSize, so this holds true too
361  */
362  ASSERT(Intsafe_CanOffsetPointer(FileHeader, pidhDosHeader->e_lfanew));
363  pinhNtHeader = (PVOID)((UINT_PTR)FileHeader + pidhDosHeader->e_lfanew);
364  }
365 
366  /*
367  * the buffer doesn't contain the NT file header, or the alignment is wrong: we
368  * need to read the header from the file
369  */
370  if(FileHeaderSize < cbFileHeaderOffsetSize ||
371  (UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
372  {
373  ULONG cbNtHeaderSize;
374  ULONG cbReadSize;
375  PVOID pData;
376 
377 l_ReadHeaderFromFile:
378  cbNtHeaderSize = 0;
379  lnOffset.QuadPart = pidhDosHeader->e_lfanew;
380 
381  /* read the header from the file */
382  nStatus = ReadFileCb(File, &lnOffset, sizeof(IMAGE_NT_HEADERS64), &pData, &pBuffer, &cbReadSize);
383 
384  if(!NT_SUCCESS(nStatus))
385  {
386  NTSTATUS ReturnedStatus = nStatus;
387 
388  /* If it attempted to read past the end of the file, it means e_lfanew is invalid */
389  if (ReturnedStatus == STATUS_END_OF_FILE) nStatus = STATUS_INVALID_IMAGE_PROTECT;
390 
391  DIE(("ReadFile failed, status %08X\n", ReturnedStatus));
392  }
393 
394  ASSERT(pData);
395  ASSERT(pBuffer);
396  ASSERT(cbReadSize > 0);
397 
398  nStatus = STATUS_INVALID_IMAGE_FORMAT;
399 
400  /* the buffer doesn't contain the file header */
401  if(cbReadSize < RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader))
402  DIE(("The file doesn't contain the PE file header\n"));
403 
404  pinhNtHeader = pData;
405 
406  /* object still not aligned: copy it to the beginning of the buffer */
407  if((UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
408  {
410  RtlMoveMemory(pBuffer, pData, cbReadSize);
411  pinhNtHeader = pBuffer;
412  }
413 
414  /* invalid NT header */
416 
417  if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
418  DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
419 
420  nStatus = STATUS_INVALID_IMAGE_FORMAT;
421 
422  if(!Intsafe_AddULong32(&cbNtHeaderSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
423  DIE(("The full NT header is too large\n"));
424 
425  /* the buffer doesn't contain the whole NT header */
426  if(cbReadSize < cbNtHeaderSize)
427  DIE(("The file doesn't contain the full NT header\n"));
428  }
429  else
430  {
431  ULONG cbOptHeaderOffsetSize = 0;
432 
434 
435  /* don't trust an invalid NT header */
436  if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
437  DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
438 
439  if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
440  DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
441 
442  nStatus = STATUS_INVALID_IMAGE_FORMAT;
443 
444  if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, cbOptHeaderOffsetSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
445  DIE(("The NT header is too large, SizeOfOptionalHeader is %X\n", pinhNtHeader->FileHeader.SizeOfOptionalHeader));
446 
447  /* the buffer doesn't contain the whole NT header: read it from the file */
448  if(cbOptHeaderOffsetSize > FileHeaderSize)
449  goto l_ReadHeaderFromFile;
450  }
451 
452  /* read information from the NT header */
453  piohOptHeader = &pinhNtHeader->OptionalHeader;
454  cbOptHeaderSize = pinhNtHeader->FileHeader.SizeOfOptionalHeader;
455 
456  nStatus = STATUS_INVALID_IMAGE_FORMAT;
457 
458  if(!RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Magic))
459  DIE(("The optional header doesn't contain the Magic field, SizeOfOptionalHeader is %X\n", cbOptHeaderSize));
460 
461  /* ASSUME: RtlZeroMemory(ImageSectionObject, sizeof(*ImageSectionObject)); */
462 
463  switch(piohOptHeader->Magic)
464  {
466 #ifndef _WIN64
467  nStatus = STATUS_INVALID_IMAGE_WIN_64;
468  DIE(("Win64 optional header, unsupported\n"));
469 #else
470  // Fall through.
471 #endif
473  break;
474  default:
475  DIE(("Unrecognized optional header, Magic is %X\n", piohOptHeader->Magic));
476  }
477 
478  if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SectionAlignment) &&
479  RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, FileAlignment))
480  {
481  /* See [1], section 3.4.2 */
482  if(piohOptHeader->SectionAlignment < PAGE_SIZE)
483  {
484  if(piohOptHeader->FileAlignment != piohOptHeader->SectionAlignment)
485  DIE(("Sections aren't page-aligned and the file alignment isn't the same\n"));
486  }
487  else if(piohOptHeader->SectionAlignment < piohOptHeader->FileAlignment)
488  DIE(("The section alignment is smaller than the file alignment\n"));
489 
490  nSectionAlignment = piohOptHeader->SectionAlignment;
491  nFileAlignment = piohOptHeader->FileAlignment;
492 
493  if(!IsPowerOf2(nSectionAlignment) || !IsPowerOf2(nFileAlignment))
494  DIE(("The section alignment (%u) and file alignment (%u) aren't both powers of 2\n", nSectionAlignment, nFileAlignment));
495  }
496  else
497  {
498  nSectionAlignment = PAGE_SIZE;
499  nFileAlignment = PAGE_SIZE;
500  }
501 
502  ASSERT(IsPowerOf2(nSectionAlignment));
503  ASSERT(IsPowerOf2(nFileAlignment));
504 
505  switch(piohOptHeader->Magic)
506  {
507  /* PE32 */
509  {
510  if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, ImageBase))
511  ImageBase = piohOptHeader->ImageBase;
512 
513  if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfImage))
514  ImageSectionObject->ImageInformation.ImageFileSize = piohOptHeader->SizeOfImage;
515 
516  if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackReserve))
517  ImageSectionObject->ImageInformation.MaximumStackSize = piohOptHeader->SizeOfStackReserve;
518 
519  if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackCommit))
520  ImageSectionObject->ImageInformation.CommittedStackSize = piohOptHeader->SizeOfStackCommit;
521 
522  if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Subsystem))
523  {
524  ImageSectionObject->ImageInformation.SubSystemType = piohOptHeader->Subsystem;
525 
526  if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MinorSubsystemVersion) &&
527  RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MajorSubsystemVersion))
528  {
529  ImageSectionObject->ImageInformation.SubSystemMinorVersion = piohOptHeader->MinorSubsystemVersion;
530  ImageSectionObject->ImageInformation.SubSystemMajorVersion = piohOptHeader->MajorSubsystemVersion;
531  }
532  }
533 
534  if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, AddressOfEntryPoint))
535  {
536  ImageSectionObject->ImageInformation.TransferAddress = (PVOID) (ImageBase +
537  piohOptHeader->AddressOfEntryPoint);
538  }
539 
540  if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfCode))
541  ImageSectionObject->ImageInformation.ImageContainsCode = piohOptHeader->SizeOfCode != 0;
542  else
543  ImageSectionObject->ImageInformation.ImageContainsCode = TRUE;
544 
545  if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, AddressOfEntryPoint))
546  {
547  if (piohOptHeader->AddressOfEntryPoint == 0)
548  {
549  ImageSectionObject->ImageInformation.ImageContainsCode = FALSE;
550  }
551  }
552 
553  if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, LoaderFlags))
554  ImageSectionObject->ImageInformation.LoaderFlags = piohOptHeader->LoaderFlags;
555 
556  if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, DllCharacteristics))
557  {
558  ImageSectionObject->ImageInformation.DllCharacteristics = piohOptHeader->DllCharacteristics;
559 
560  /*
561  * Since we don't really implement SxS yet and LD doesn't supoprt /ALLOWISOLATION:NO, hard-code
562  * this flag here, which will prevent the loader and other code from doing any .manifest or SxS
563  * magic to any binary.
564  *
565  * This will break applications that depend on SxS when running with real Windows Kernel32/SxS/etc
566  * but honestly that's not tested. It will also break them when running no ReactOS once we implement
567  * the SxS support -- at which point, duh, this should be removed.
568  *
569  * But right now, any app depending on SxS is already broken anyway, so this flag only helps.
570  */
571  ImageSectionObject->ImageInformation.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_NO_ISOLATION;
572  }
573 
574  break;
575  }
576 #ifdef _WIN64
577  /* PE64 */
579  {
580  const IMAGE_OPTIONAL_HEADER64 * pioh64OptHeader;
581 
582  pioh64OptHeader = (const IMAGE_OPTIONAL_HEADER64 *)piohOptHeader;
583 
584  if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, ImageBase))
585  {
586  ImageBase = pioh64OptHeader->ImageBase;
587  if(pioh64OptHeader->ImageBase > MAXULONG_PTR)
588  DIE(("ImageBase exceeds the address space\n"));
589  }
590 
591  if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfImage))
592  {
593  if(pioh64OptHeader->SizeOfImage > MAXULONG_PTR)
594  DIE(("SizeOfImage exceeds the address space\n"));
595 
596  ImageSectionObject->ImageInformation.ImageFileSize = pioh64OptHeader->SizeOfImage;
597  }
598 
599  if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackReserve))
600  {
601  if(pioh64OptHeader->SizeOfStackReserve > MAXULONG_PTR)
602  DIE(("SizeOfStackReserve exceeds the address space\n"));
603 
604  ImageSectionObject->ImageInformation.MaximumStackSize = (ULONG_PTR) pioh64OptHeader->SizeOfStackReserve;
605  }
606 
607  if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackCommit))
608  {
609  if(pioh64OptHeader->SizeOfStackCommit > MAXULONG_PTR)
610  DIE(("SizeOfStackCommit exceeds the address space\n"));
611 
612  ImageSectionObject->ImageInformation.CommittedStackSize = (ULONG_PTR) pioh64OptHeader->SizeOfStackCommit;
613  }
614 
615  if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, Subsystem))
616  {
617  ImageSectionObject->ImageInformation.SubSystemType = pioh64OptHeader->Subsystem;
618 
619  if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, MinorSubsystemVersion) &&
620  RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, MajorSubsystemVersion))
621  {
622  ImageSectionObject->ImageInformation.SubSystemMinorVersion = pioh64OptHeader->MinorSubsystemVersion;
623  ImageSectionObject->ImageInformation.SubSystemMajorVersion = pioh64OptHeader->MajorSubsystemVersion;
624  }
625  }
626 
627  if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, AddressOfEntryPoint))
628  {
629  ImageSectionObject->ImageInformation.TransferAddress = (PVOID) (ImageBase +
630  pioh64OptHeader->AddressOfEntryPoint);
631  }
632 
633  if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfCode))
634  ImageSectionObject->ImageInformation.ImageContainsCode = pioh64OptHeader->SizeOfCode != 0;
635  else
636  ImageSectionObject->ImageInformation.ImageContainsCode = TRUE;
637 
638  if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, AddressOfEntryPoint))
639  {
640  if (pioh64OptHeader->AddressOfEntryPoint == 0)
641  {
642  ImageSectionObject->ImageInformation.ImageContainsCode = FALSE;
643  }
644  }
645 
646  if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, LoaderFlags))
647  ImageSectionObject->ImageInformation.LoaderFlags = pioh64OptHeader->LoaderFlags;
648 
649  if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, DllCharacteristics))
650  ImageSectionObject->ImageInformation.DllCharacteristics = pioh64OptHeader->DllCharacteristics;
651 
652  break;
653  }
654 #endif // _WIN64
655  }
656 
657  /* [1], section 3.4.2 */
658  if((ULONG_PTR)ImageBase % 0x10000)
659  DIE(("ImageBase is not aligned on a 64KB boundary"));
660 
661  ImageSectionObject->ImageInformation.ImageCharacteristics = pinhNtHeader->FileHeader.Characteristics;
662  ImageSectionObject->ImageInformation.Machine = pinhNtHeader->FileHeader.Machine;
663  ImageSectionObject->ImageInformation.GpValue = 0;
664  ImageSectionObject->ImageInformation.ZeroBits = 0;
665  ImageSectionObject->BasedAddress = (PVOID)ImageBase;
666 
667  /* SECTION HEADERS */
668  nStatus = STATUS_INVALID_IMAGE_FORMAT;
669 
670  /* see [1], section 3.3 */
671  if(pinhNtHeader->FileHeader.NumberOfSections > 96)
672  DIE(("Too many sections, NumberOfSections is %u\n", pinhNtHeader->FileHeader.NumberOfSections));
673 
674  /*
675  * the additional segment is for the file's headers. They need to be present for
676  * the benefit of the dynamic loader (to locate exports, defaults for thread
677  * parameters, resources, etc.)
678  */
679  ImageSectionObject->NrSegments = pinhNtHeader->FileHeader.NumberOfSections + 1;
680 
681  /* file offset for the section headers */
682  if(!Intsafe_AddULong32(&cbSectionHeadersOffset, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
683  DIE(("Offset overflow\n"));
684 
685  if(!Intsafe_AddULong32(&cbSectionHeadersOffset, cbSectionHeadersOffset, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
686  DIE(("Offset overflow\n"));
687 
688  /* size of the section headers */
690  cbSectionHeadersSize = pinhNtHeader->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
691 
692  if(!Intsafe_AddULong32(&cbSectionHeadersOffsetSize, cbSectionHeadersOffset, cbSectionHeadersSize))
693  DIE(("Section headers too large\n"));
694 
695  /* size of the executable's headers */
696  if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfHeaders))
697  {
698 // if(!IsAligned(piohOptHeader->SizeOfHeaders, nFileAlignment))
699 // DIE(("SizeOfHeaders is not aligned\n"));
700 
701  if(cbSectionHeadersSize > piohOptHeader->SizeOfHeaders)
702  DIE(("The section headers overflow SizeOfHeaders\n"));
703 
704  cbHeadersSize = piohOptHeader->SizeOfHeaders;
705  }
706  else if(!AlignUp(&cbHeadersSize, cbSectionHeadersOffsetSize, nFileAlignment))
707  DIE(("Overflow aligning the size of headers\n"));
708 
709  if(pBuffer)
710  {
712  pBuffer = NULL;
713  }
714  /* WARNING: pinhNtHeader IS NO LONGER USABLE */
715  /* WARNING: piohOptHeader IS NO LONGER USABLE */
716  /* WARNING: pioh64OptHeader IS NO LONGER USABLE */
717 
718  if(FileHeaderSize < cbSectionHeadersOffsetSize)
719  pishSectionHeaders = NULL;
720  else
721  {
722  /*
723  * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
724  * and FileHeaderSize >= cbSectionHeadersOffsetSize, so this holds true too
725  */
726  ASSERT(Intsafe_CanOffsetPointer(FileHeader, cbSectionHeadersOffset));
727  pishSectionHeaders = (PVOID)((UINT_PTR)FileHeader + cbSectionHeadersOffset);
728  }
729 
730  /*
731  * the buffer doesn't contain the section headers, or the alignment is wrong:
732  * read the headers from the file
733  */
734  if(FileHeaderSize < cbSectionHeadersOffsetSize ||
735  (UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0)
736  {
737  PVOID pData;
738  ULONG cbReadSize;
739 
740  lnOffset.QuadPart = cbSectionHeadersOffset;
741 
742  /* read the header from the file */
743  nStatus = ReadFileCb(File, &lnOffset, cbSectionHeadersSize, &pData, &pBuffer, &cbReadSize);
744 
745  if(!NT_SUCCESS(nStatus))
746  DIE(("ReadFile failed with status %08X\n", nStatus));
747 
748  ASSERT(pData);
749  ASSERT(pBuffer);
750  ASSERT(cbReadSize > 0);
751 
752  nStatus = STATUS_INVALID_IMAGE_FORMAT;
753 
754  /* the buffer doesn't contain all the section headers */
755  if(cbReadSize < cbSectionHeadersSize)
756  DIE(("The file doesn't contain all of the section headers\n"));
757 
758  pishSectionHeaders = pData;
759 
760  /* object still not aligned: copy it to the beginning of the buffer */
761  if((UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0)
762  {
764  RtlMoveMemory(pBuffer, pData, cbReadSize);
765  pishSectionHeaders = pBuffer;
766  }
767  }
768 
769  /* SEGMENTS */
770  /* allocate the segments */
772  ImageSectionObject->Segments = AllocateSegmentsCb(ImageSectionObject->NrSegments);
773 
774  if(ImageSectionObject->Segments == NULL)
775  DIE(("AllocateSegments failed\n"));
776 
777  /* initialize the headers segment */
778  pssSegments = ImageSectionObject->Segments;
779 
780 // ASSERT(IsAligned(cbHeadersSize, nFileAlignment));
781 
782  if(!AlignUp(&nFileSizeOfHeaders, cbHeadersSize, nFileAlignment))
783  DIE(("Cannot align the size of the section headers\n"));
784 
785  nPrevVirtualEndOfSegment = ALIGN_UP_BY(cbHeadersSize, nSectionAlignment);
786  if (nPrevVirtualEndOfSegment < cbHeadersSize)
787  DIE(("Cannot align the size of the section headers\n"));
788 
789  pssSegments[0].Image.FileOffset = 0;
790  pssSegments[0].Protection = PAGE_READONLY;
791  pssSegments[0].Length.QuadPart = nPrevVirtualEndOfSegment;
792  pssSegments[0].RawLength.QuadPart = nFileSizeOfHeaders;
793  pssSegments[0].Image.VirtualAddress = 0;
794  pssSegments[0].Image.Characteristics = 0;
795  pssSegments[0].WriteCopy = TRUE;
796 
797  /* skip the headers segment */
798  ++ pssSegments;
799 
800  nStatus = STATUS_INVALID_IMAGE_FORMAT;
801 
802  ASSERT(ImageSectionObject->RefCount > 0);
803 
804  /* convert the executable sections into segments. See also [1], section 4 */
805  for(i = 0; i < ImageSectionObject->NrSegments - 1; ++ i)
806  {
807  ULONG nCharacteristics;
808 
809  /* validate the alignment */
810  if(!IsAligned(pishSectionHeaders[i].VirtualAddress, nSectionAlignment))
811  DIE(("Image.VirtualAddress[%u] is not aligned\n", i));
812 
813  /* sections must be contiguous, ordered by base address and non-overlapping */
814  if(pishSectionHeaders[i].VirtualAddress != nPrevVirtualEndOfSegment)
815  DIE(("Memory gap between section %u and the previous\n", i));
816 
817  /* ignore explicit BSS sections */
818  if(pishSectionHeaders[i].SizeOfRawData != 0)
819  {
820  /* validate the alignment */
821 #if 0
822  /* Yes, this should be a multiple of FileAlignment, but there's
823  * stuff out there that isn't. We can cope with that
824  */
825  if(!IsAligned(pishSectionHeaders[i].SizeOfRawData, nFileAlignment))
826  DIE(("SizeOfRawData[%u] is not aligned\n", i));
827 #endif
828 
829 // if(!IsAligned(pishSectionHeaders[i].PointerToRawData, nFileAlignment))
830 // DIE(("PointerToRawData[%u] is not aligned\n", i));
831 
832  if(!Intsafe_CanAddULong32(pishSectionHeaders[i].PointerToRawData, pishSectionHeaders[i].SizeOfRawData))
833  DIE(("SizeOfRawData[%u] too large\n", i));
834 
835  /* conversion */
836  pssSegments[i].Image.FileOffset = pishSectionHeaders[i].PointerToRawData;
837  pssSegments[i].RawLength.QuadPart = pishSectionHeaders[i].SizeOfRawData;
838  }
839  else
840  {
841  /* FIXME: Should reset PointerToRawData to 0 in the image mapping */
842  ASSERT(pssSegments[i].Image.FileOffset == 0);
843  ASSERT(pssSegments[i].RawLength.QuadPart == 0);
844  }
845 
846  ASSERT(Intsafe_CanAddLong64(pssSegments[i].Image.FileOffset, pssSegments[i].RawLength.QuadPart));
847 
848  nCharacteristics = pishSectionHeaders[i].Characteristics;
849 
850  /* no explicit protection */
851  if((nCharacteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)) == 0)
852  {
853  if(nCharacteristics & IMAGE_SCN_CNT_CODE)
854  nCharacteristics |= IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ;
855 
856  if(nCharacteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
857  nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
858 
859  if(nCharacteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
860  nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
861  }
862 
863  /* see table above */
864  pssSegments[i].Protection = SectionCharacteristicsToProtect[nCharacteristics >> 28];
865  pssSegments[i].WriteCopy = !(nCharacteristics & IMAGE_SCN_MEM_SHARED);
866 
867  if(pishSectionHeaders[i].Misc.VirtualSize == 0)
868  pssSegments[i].Length.QuadPart = pishSectionHeaders[i].SizeOfRawData;
869  else
870  pssSegments[i].Length.QuadPart = pishSectionHeaders[i].Misc.VirtualSize;
871 
872  AlignedLength = ALIGN_UP_BY(pssSegments[i].Length.LowPart, nSectionAlignment);
873  if(AlignedLength < pssSegments[i].Length.LowPart)
874  DIE(("Cannot align the virtual size of section %u\n", i));
875 
876  pssSegments[i].Length.LowPart = AlignedLength;
877 
878  if(pssSegments[i].Length.QuadPart == 0)
879  DIE(("Virtual size of section %u is null\n", i));
880 
881  pssSegments[i].Image.VirtualAddress = pishSectionHeaders[i].VirtualAddress;
882  pssSegments[i].Image.Characteristics = pishSectionHeaders[i].Characteristics;
883 
884  /* ensure the memory image is no larger than 4GB */
885  nPrevVirtualEndOfSegment = (ULONG_PTR)(pssSegments[i].Image.VirtualAddress + pssSegments[i].Length.QuadPart);
886  if (nPrevVirtualEndOfSegment < pssSegments[i].Image.VirtualAddress)
887  DIE(("The image is too large\n"));
888  }
889 
890  if(nSectionAlignment >= PAGE_SIZE)
892 
893  /* Success */
894  nStatus = STATUS_SUCCESS;// STATUS_ROS_EXEFMT_LOADED_FORMAT | EXEFMT_LOADED_PE32;
895 
896 l_Return:
897  if(pBuffer)
899 
900  return nStatus;
901 }
902 
903 /*
904  * FUNCTION: Waits in kernel mode indefinitely for a file object lock.
905  * ARGUMENTS: PFILE_OBJECT to wait for.
906  * RETURNS: Status of the wait.
907  */
908 NTSTATUS
910 {
911  return STATUS_SUCCESS;
912  //return KeWaitForSingleObject(&File->Lock, 0, KernelMode, FALSE, NULL);
913 }
914 
915 
916 
917 VOID
918 NTAPI
920 {
921  ULONG Length;
924  SWAPENTRY SavedSwapEntry;
926 
927  Page = 0;
928 
930 
931  Length = PAGE_ROUND_UP(Segment->Length.QuadPart);
932  for (Offset.QuadPart = 0; Offset.QuadPart < Length; Offset.QuadPart += PAGE_SIZE)
933  {
935  if (Entry)
936  {
938  if (IS_SWAP_FROM_SSE(Entry))
939  {
941  }
942  else
943  {
945  SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
946  if (SavedSwapEntry != 0)
947  {
949  MmFreeSwapPage(SavedSwapEntry);
950  }
952  }
953  }
954  }
955 
957 }
958 
959 static
960 VOID
961 NTAPI
963 {
966 
968 
970 
972 
973  /* This must be either a valid entry or nothing */
975 
976  /* There should be no reference anymore */
978 
980  /* If there is a page, this must be because it's still dirty */
981  ASSERT(Page != 0);
982 
983  /* Write the page */
984  if (IS_DIRTY_SSE(Entry))
985  MiWritePage(Segment, Offset->QuadPart, Page);
986 
988 }
989 
995 VOID
996 NTAPI
997 MmDereferenceSegmentWithLock(
1000 {
1001  /* Lock the PFN lock because we mess around with SectionObjectPointers */
1002  if (OldIrql == MM_NOIRQL)
1003  {
1004  OldIrql = MiAcquirePfnLock();
1005  }
1006 
1007  if (InterlockedDecrement64(Segment->ReferenceCount) > 0)
1008  {
1009  /* Nothing to do yet */
1010  MiReleasePfnLock(OldIrql);
1011  return;
1012  }
1013 
1014  *Segment->Flags |= MM_SEGMENT_INDELETE;
1015 
1016  /* Flush the segment */
1017  if (*Segment->Flags & MM_DATAFILE_SEGMENT)
1018  {
1019  MiReleasePfnLock(OldIrql);
1020  /* Free the page table. This will flush any remaining dirty data */
1022 
1023  OldIrql = MiAcquirePfnLock();
1024  /* Delete the pointer on the file */
1025  ASSERT(Segment->FileObject->SectionObjectPointer->DataSectionObject == Segment);
1026  Segment->FileObject->SectionObjectPointer->DataSectionObject = NULL;
1027  MiReleasePfnLock(OldIrql);
1028  ObDereferenceObject(Segment->FileObject);
1029 
1031  }
1032  else
1033  {
1034  /* Most grotesque thing ever */
1035  PMM_IMAGE_SECTION_OBJECT ImageSectionObject = CONTAINING_RECORD(Segment->ReferenceCount, MM_IMAGE_SECTION_OBJECT, RefCount);
1036  PMM_SECTION_SEGMENT SectionSegments;
1037  ULONG NrSegments;
1038  ULONG i;
1039 
1040  /* Delete the pointer on the file */
1041  ASSERT(ImageSectionObject->FileObject->SectionObjectPointer->ImageSectionObject == ImageSectionObject);
1042  ImageSectionObject->FileObject->SectionObjectPointer->ImageSectionObject = NULL;
1043  MiReleasePfnLock(OldIrql);
1044 
1045  ObDereferenceObject(ImageSectionObject->FileObject);
1046 
1047  NrSegments = ImageSectionObject->NrSegments;
1048  SectionSegments = ImageSectionObject->Segments;
1049  for (i = 0; i < NrSegments; i++)
1050  {
1051  if (SectionSegments[i].Image.Characteristics & IMAGE_SCN_MEM_SHARED)
1052  {
1053  MmpFreePageFileSegment(&SectionSegments[i]);
1054  }
1055 
1056  MmFreePageTablesSectionSegment(&SectionSegments[i], NULL);
1057  }
1058 
1059  ExFreePoolWithTag(ImageSectionObject->Segments, TAG_MM_SECTION_SEGMENT);
1060  ExFreePoolWithTag(ImageSectionObject, TAG_MM_SECTION_SEGMENT);
1061  }
1062 }
1063 
1064 VOID
1065 NTAPI
1068 {
1069  ULONG_PTR Entry;
1070 
1072  if (Entry == 0)
1073  {
1074  DPRINT1("Entry == 0 for MmSharePageEntrySectionSegment\n");
1075  KeBugCheck(MEMORY_MANAGEMENT);
1076  }
1078  {
1079  DPRINT1("Maximum share count reached\n");
1080  KeBugCheck(MEMORY_MANAGEMENT);
1081  }
1082  if (IS_SWAP_FROM_SSE(Entry))
1083  {
1084  KeBugCheck(MEMORY_MANAGEMENT);
1085  }
1087 }
1088 
1089 BOOLEAN
1090 NTAPI
1094  BOOLEAN Dirty,
1095  BOOLEAN PageOut,
1096  ULONG_PTR *InEntry)
1097 {
1098  ULONG_PTR Entry = InEntry ? *InEntry : MmGetPageEntrySectionSegment(Segment, Offset);
1100  BOOLEAN IsDataMap = BooleanFlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT);
1101  SWAPENTRY SwapEntry;
1102 
1103  if (Entry == 0)
1104  {
1105  DPRINT1("Entry == 0 for MmUnsharePageEntrySectionSegment\n");
1106  KeBugCheck(MEMORY_MANAGEMENT);
1107  }
1108  if (SHARE_COUNT_FROM_SSE(Entry) == 0)
1109  {
1110  DPRINT1("Zero share count for unshare (Seg %p Offset %x Page %x)\n", Segment, Offset->LowPart, PFN_FROM_SSE(Entry));
1111  KeBugCheck(MEMORY_MANAGEMENT);
1112  }
1113  if (IS_SWAP_FROM_SSE(Entry))
1114  {
1115  KeBugCheck(MEMORY_MANAGEMENT);
1116  }
1117  Entry = DECREF_SSE(Entry);
1118  if (Dirty) Entry = DIRTY_SSE(Entry);
1119 
1120  /* If we are paging-out, pruning the page for real will be taken care of in MmCheckDirtySegment */
1121  if ((SHARE_COUNT_FROM_SSE(Entry) > 0) || PageOut)
1122  {
1123  /* Update the page mapping in the segment and we're done */
1125  return FALSE;
1126  }
1127 
1128  /* We are pruning the last mapping on this page. See if we can keep it a bit more. */
1129  ASSERT(!PageOut);
1130 
1131  if (IsDataMap)
1132  {
1133  /* We can always keep memory in for data maps */
1135  return FALSE;
1136  }
1137 
1138  if (!FlagOn(Segment->Image.Characteristics, IMAGE_SCN_MEM_SHARED))
1139  {
1140  ASSERT(Segment->WriteCopy);
1143  /* So this must have been a read-only page. Keep it ! */
1145  return FALSE;
1146  }
1147 
1148  /*
1149  * So this is a page for a shared section of a DLL.
1150  * We can keep it if it is not dirty.
1151  */
1152  SwapEntry = MmGetSavedSwapEntryPage(Page);
1153  if ((SwapEntry == 0) && !IS_DIRTY_SSE(Entry))
1154  {
1156  return FALSE;
1157  }
1158 
1159  /* No more processes are referencing this shared dirty page. Ditch it. */
1160  if (SwapEntry)
1161  {
1163  MmFreeSwapPage(SwapEntry);
1164  }
1167  return TRUE;
1168 }
1169 
1170 static
1171 NTSTATUS
1172 MiCopyFromUserPage(PFN_NUMBER DestPage, const VOID *SrcAddress)
1173 {
1175  KIRQL Irql;
1176  PVOID DestAddress;
1177 
1179  DestAddress = MiMapPageInHyperSpace(Process, DestPage, &Irql);
1180  if (DestAddress == NULL)
1181  {
1182  return STATUS_NO_MEMORY;
1183  }
1184  ASSERT((ULONG_PTR)DestAddress % PAGE_SIZE == 0);
1185  ASSERT((ULONG_PTR)SrcAddress % PAGE_SIZE == 0);
1186  RtlCopyMemory(DestAddress, SrcAddress, PAGE_SIZE);
1187  MiUnmapPageInHyperSpace(Process, DestAddress, Irql);
1188  return STATUS_SUCCESS;
1189 }
1190 
1191 static
1192 NTSTATUS
1193 NTAPI
1197  _In_ ULONG Length,
1198  _In_opt_ PLARGE_INTEGER ValidDataLength,
1199  _In_ BOOLEAN SetDirty)
1200 {
1201  /* Let's use a 64K granularity. */
1202  LONGLONG RangeStart, RangeEnd;
1203  NTSTATUS Status;
1204  PFILE_OBJECT FileObject = Segment->FileObject;
1205 
1206  /* Calculate our range, aligned on 64K if possible. */
1207  Status = RtlLongLongAdd(Offset, Length, &RangeEnd);
1209  if (!NT_SUCCESS(Status))
1210  return Status;
1211 
1212  /* If the file is not random access and we are not the page out thread
1213  * read a 64K Chunk. */
1215  && !FlagOn(FileObject->Flags, FO_RANDOM_ACCESS))
1216  {
1217  RangeStart = Offset - (Offset % _64K);
1218  if (RangeEnd % _64K)
1219  RangeEnd += _64K - (RangeEnd % _64K);
1220  }
1221  else
1222  {
1223  RangeStart = Offset - (Offset % PAGE_SIZE);
1224  if (RangeEnd % PAGE_SIZE)
1225  RangeEnd += PAGE_SIZE - (RangeEnd % PAGE_SIZE);
1226  }
1227 
1228  /* Clamp if needed */
1229  if (!FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT))
1230  {
1231  if (RangeEnd > Segment->RawLength.QuadPart)
1232  RangeEnd = Segment->RawLength.QuadPart;
1233  }
1234 
1235  /* Let's gooooooooo */
1236  for ( ; RangeStart < RangeEnd; RangeStart += _64K)
1237  {
1238  /* First take a look at where we miss pages */
1239  ULONG ToReadPageBits = 0;
1240  LONGLONG ChunkEnd = RangeStart + _64K;
1241 
1242  if (ChunkEnd > RangeEnd)
1243  ChunkEnd = RangeEnd;
1244 
1246  for (LONGLONG ChunkOffset = RangeStart; ChunkOffset < ChunkEnd; ChunkOffset += PAGE_SIZE)
1247  {
1248  LARGE_INTEGER CurrentOffset;
1249 
1250  CurrentOffset.QuadPart = ChunkOffset;
1252 
1253  /* Let any pending read proceed */
1254  while (MM_IS_WAIT_PTE(Entry))
1255  {
1257 
1259 
1261  Entry = MmGetPageEntrySectionSegment(Segment, &CurrentOffset);
1262  }
1263 
1264  if (Entry != 0)
1265  {
1266  /* Dirtify it if it's a resident page and we're asked to */
1267  if (SetDirty && !IS_SWAP_FROM_SSE(Entry))
1269  continue;
1270  }
1271 
1272  ToReadPageBits |= 1UL << ((ChunkOffset - RangeStart) >> PAGE_SHIFT);
1273 
1274  /* Put a wait entry here */
1275  MmSetPageEntrySectionSegment(Segment, &CurrentOffset, MAKE_SWAP_SSE(MM_WAIT_ENTRY));
1276  }
1278 
1279  if (ToReadPageBits == 0)
1280  {
1281  /* Nothing to do for this chunk */
1282  continue;
1283  }
1284 
1285  /* Now perform the actual read */
1286  LONGLONG ChunkOffset = RangeStart;
1287  while (ChunkOffset < ChunkEnd)
1288  {
1289  /* Move forward if there is a hole */
1290  ULONG BitSet;
1291  if (!_BitScanForward(&BitSet, ToReadPageBits))
1292  {
1293  /* Nothing more to read */
1294  break;
1295  }
1296  ToReadPageBits >>= BitSet;
1297  ChunkOffset += BitSet * PAGE_SIZE;
1298  ASSERT(ChunkOffset < ChunkEnd);
1299 
1300  /* Get the range we have to read */
1301  _BitScanForward(&BitSet, ~ToReadPageBits);
1302  ULONG ReadLength = BitSet * PAGE_SIZE;
1303 
1304  ASSERT(ReadLength <= _64K);
1305 
1306  /* Clamp (This is for image mappings */
1307  if ((ChunkOffset + ReadLength) > ChunkEnd)
1308  ReadLength = ChunkEnd - ChunkOffset;
1309 
1310  ASSERT(ReadLength != 0);
1311 
1312  /* Allocate a MDL */
1314  if (!Mdl)
1315  {
1316  /* Damn. Roll-back. */
1318  while (ChunkOffset < ChunkEnd)
1319  {
1320  if (ToReadPageBits & 1)
1321  {
1322  LARGE_INTEGER CurrentOffset;
1323  CurrentOffset.QuadPart = ChunkOffset;
1325  MmSetPageEntrySectionSegment(Segment, &CurrentOffset, 0);
1326  }
1327  ToReadPageBits >>= 1;
1328  ChunkOffset += PAGE_SIZE;
1329  }
1332  }
1333 
1334  /* Get our pages */
1335  PPFN_NUMBER Pages = MmGetMdlPfnArray(Mdl);
1337  for (UINT i = 0; i < BYTES_TO_PAGES(ReadLength); i++)
1338  {
1339  /* MmRequestPageMemoryConsumer succeeds or bugchecks */
1341  }
1342  Mdl->MdlFlags |= MDL_PAGES_LOCKED | MDL_IO_PAGE_READ;
1343 
1345  FileOffset.QuadPart = Segment->Image.FileOffset + ChunkOffset;
1346 
1347  /* Clamp to VDL */
1348  if (ValidDataLength && ((FileOffset.QuadPart + ReadLength) > ValidDataLength->QuadPart))
1349  {
1350  if (FileOffset.QuadPart > ValidDataLength->QuadPart)
1351  {
1352  /* Great, nothing to read. */
1353  goto AssignPagesToSegment;
1354  }
1355 
1356  Mdl->Size = (FileOffset.QuadPart + ReadLength) - ValidDataLength->QuadPart;
1357  }
1358 
1359  KEVENT Event;
1361 
1362  /* Disable APCs */
1363  KIRQL OldIrql;
1365 
1368  if (Status == STATUS_PENDING)
1369  {
1371  Status = Iosb.Status;
1372  }
1373 
1374  if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
1375  {
1376  MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
1377  }
1378 
1380 
1381  if (Status == STATUS_END_OF_FILE)
1382  {
1383  DPRINT1("Got STATUS_END_OF_FILE at offset %I64d for file %wZ.\n", FileOffset.QuadPart, &FileObject->FileName);
1385  }
1386 
1387  if (!NT_SUCCESS(Status))
1388  {
1389  /* Damn. Roll back. */
1390  for (UINT i = 0; i < BYTES_TO_PAGES(ReadLength); i++)
1392 
1394  while (ChunkOffset < ChunkEnd)
1395  {
1396  if (ToReadPageBits & 1)
1397  {
1398  LARGE_INTEGER CurrentOffset;
1399  CurrentOffset.QuadPart = ChunkOffset;
1401  MmSetPageEntrySectionSegment(Segment, &CurrentOffset, 0);
1402  }
1403  ToReadPageBits >>= 1;
1404  ChunkOffset += PAGE_SIZE;
1405  }
1407  IoFreeMdl(Mdl);;
1408  return Status;
1409  }
1410 
1411 AssignPagesToSegment:
1413 
1414  for (UINT i = 0; i < BYTES_TO_PAGES(ReadLength); i++)
1415  {
1416  ULONG_PTR Entry = MAKE_SSE(Pages[i] << PAGE_SHIFT, 0);
1417  LARGE_INTEGER CurrentOffset;
1418  CurrentOffset.QuadPart = ChunkOffset + (i * PAGE_SIZE);
1419 
1421 
1422  if (SetDirty)
1423  Entry = DIRTY_SSE(Entry);
1424 
1425  MmSetPageEntrySectionSegment(Segment, &CurrentOffset, Entry);
1426  }
1427 
1429 
1430  IoFreeMdl(Mdl);
1431  ToReadPageBits >>= BitSet;
1432  ChunkOffset += BitSet * PAGE_SIZE;
1433  }
1434  }
1435 
1436  return STATUS_SUCCESS;
1437 }
1438 
1439 static VOID
1443  ULONG OldType,
1444  ULONG OldProtect,
1445  ULONG NewType,
1446  ULONG NewProtect)
1447 {
1450  BOOLEAN DoCOW = FALSE;
1451  ULONG i;
1453 
1455  ASSERT(MemoryArea != NULL);
1456  Segment = MemoryArea->SectionData.Segment;
1458 
1459  if ((Segment->WriteCopy) &&
1461  {
1462  DoCOW = TRUE;
1463  }
1464 
1465  if (OldProtect != NewProtect)
1466  {
1467  for (i = 0; i < PAGE_ROUND_UP(RegionSize) / PAGE_SIZE; i++)
1468  {
1469  SWAPENTRY SwapEntry;
1470  PVOID Address = (char*)BaseAddress + (i * PAGE_SIZE);
1472 
1473  /* Wait for a wait entry to disappear */
1474  do
1475  {
1476  MmGetPageFileMapping(Process, Address, &SwapEntry);
1477  if (SwapEntry != MM_WAIT_ENTRY)
1478  break;
1481  YieldProcessor();
1484  }
1485  while (TRUE);
1486 
1487  /*
1488  * If we doing COW for this segment then check if the page is
1489  * already private.
1490  */
1492  {
1494  ULONG_PTR Entry;
1495  PFN_NUMBER Page;
1496 
1498  + MemoryArea->SectionData.ViewOffset;
1500  /*
1501  * An MM_WAIT_ENTRY is ok in this case... It'll just count as
1502  * IS_SWAP_FROM_SSE and we'll do the right thing.
1503  */
1505 
1508  {
1509  Protect = NewProtect;
1510  }
1511  }
1512 
1514  {
1516  Protect);
1517  }
1518  }
1519  }
1520 
1522 }
1523 
1524 NTSTATUS
1525 NTAPI
1528  PVOID Address,
1529  BOOLEAN Locked)
1530 {
1532  PFN_NUMBER Page;
1533  NTSTATUS Status;
1535  ULONG_PTR Entry;
1536  ULONG_PTR Entry1;
1537  ULONG Attributes;
1539  BOOLEAN HasSwapEntry;
1540  PVOID PAddress;
1542  SWAPENTRY SwapEntry;
1543 
1544  ASSERT(Locked);
1545 
1546  /*
1547  * There is a window between taking the page fault and locking the
1548  * address space when another thread could load the page so we check
1549  * that.
1550  */
1552  {
1553  return STATUS_SUCCESS;
1554  }
1555 
1557  {
1558  return STATUS_ACCESS_VIOLATION;
1559  }
1560 
1561  /*
1562  * Check for the virtual memory area being deleted.
1563  */
1565  {
1566  return STATUS_UNSUCCESSFUL;
1567  }
1568 
1569  PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1570  Offset.QuadPart = (ULONG_PTR)PAddress - MA_GetStartingAddress(MemoryArea)
1571  + MemoryArea->SectionData.ViewOffset;
1572 
1573  Segment = MemoryArea->SectionData.Segment;
1575  &MemoryArea->SectionData.RegionListHead,
1576  Address, NULL);
1577  ASSERT(Region != NULL);
1578 
1579  /* Check for a NOACCESS mapping */
1580  if (Region->Protect & PAGE_NOACCESS)
1581  {
1582  return STATUS_ACCESS_VIOLATION;
1583  }
1584 
1585  if (Region->Protect & PAGE_GUARD)
1586  {
1587  /* Remove it */
1589  &MemoryArea->SectionData.RegionListHead,
1590  Address, PAGE_SIZE, Region->Type, Region->Protect & ~PAGE_GUARD,
1592 
1593  if (!NT_SUCCESS(Status))
1594  {
1595  DPRINT1("Removing PAGE_GUARD protection failed : 0x%08x.\n", Status);
1596  }
1597 
1599  }
1600 
1601  HasSwapEntry = MmIsPageSwapEntry(Process, Address);
1602 
1603  /* See if we should use a private page */
1604  if (HasSwapEntry)
1605  {
1606  SWAPENTRY DummyEntry;
1607 
1608  MmGetPageFileMapping(Process, Address, &SwapEntry);
1609  if (SwapEntry == MM_WAIT_ENTRY)
1610  {
1612  YieldProcessor();
1615  }
1616 
1617  /*
1618  * Must be private page we have swapped out.
1619  */
1620 
1621  /*
1622  * Sanity check
1623  */
1624  MmDeletePageFileMapping(Process, Address, &DummyEntry);
1625  ASSERT(DummyEntry == SwapEntry);
1626 
1627  /* Tell everyone else we are serving the fault. */
1628  MmCreatePageFileMapping(Process, Address, MM_WAIT_ENTRY);
1629 
1632  if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1633  if (!Process) MI_SET_PROCESS2("Kernel Section");
1635  if (!NT_SUCCESS(Status))
1636  {
1637  KeBugCheck(MEMORY_MANAGEMENT);
1638  }
1639 
1640  Status = MmReadFromSwapPage(SwapEntry, Page);
1641  if (!NT_SUCCESS(Status))
1642  {
1643  DPRINT1("MmReadFromSwapPage failed, status = %x\n", Status);
1644  KeBugCheck(MEMORY_MANAGEMENT);
1645  }
1646 
1648  MmDeletePageFileMapping(Process, PAddress, &DummyEntry);
1649  ASSERT(DummyEntry == MM_WAIT_ENTRY);
1650 
1652  PAddress,
1653  Region->Protect,
1654  Page);
1655  if (!NT_SUCCESS(Status))
1656  {
1657  DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
1658  KeBugCheck(MEMORY_MANAGEMENT);
1659  return Status;
1660  }
1661 
1662  /*
1663  * Store the swap entry for later use.
1664  */
1665  MmSetSavedSwapEntryPage(Page, SwapEntry);
1666 
1667  /*
1668  * Add the page to the process's working set
1669  */
1671  /*
1672  * Finish the operation
1673  */
1674  DPRINT("Address 0x%p\n", Address);
1675  return STATUS_SUCCESS;
1676  }
1677 
1678  /*
1679  * Lock the segment
1680  */
1682 
1683  /*
1684  * Satisfying a page fault on a map of /Device/PhysicalMemory is easy
1685  */
1686  if ((*Segment->Flags) & MM_PHYSICALMEMORY_SEGMENT)
1687  {
1689  /*
1690  * Just map the desired physical page
1691  */
1692  Page = (PFN_NUMBER)(Offset.QuadPart >> PAGE_SHIFT);
1694  PAddress,
1695  Region->Protect,
1696  Page);
1697  if (!NT_SUCCESS(Status))
1698  {
1699  DPRINT("MmCreateVirtualMappingUnsafe failed, not out of memory\n");
1700  KeBugCheck(MEMORY_MANAGEMENT);
1701  return Status;
1702  }
1703 
1704  /*
1705  * Cleanup and release locks
1706  */
1707  DPRINT("Address 0x%p\n", Address);
1708  return STATUS_SUCCESS;
1709  }
1710 
1711  /*
1712  * Check if this page needs to be mapped COW
1713  */
1714  if ((Segment->WriteCopy) &&
1715  (Region->Protect == PAGE_READWRITE || Region->Protect == PAGE_EXECUTE_READWRITE))
1716  {
1718  }
1719  else
1720  {
1721  Attributes = Region->Protect;
1722  }
1723 
1724 
1725  /*
1726  * Get the entry corresponding to the offset within the section
1727  */
1729  if (Entry == 0)
1730  {
1731  /*
1732  * If the entry is zero, then we need to load the page.
1733  */
1734  if ((Offset.QuadPart >= (LONGLONG)PAGE_ROUND_UP(Segment->RawLength.QuadPart)) && (MemoryArea->VadNode.u.VadFlags.VadType == VadImageMap))
1735  {
1736  /* We are beyond the data which is on file. Just get a new page. */
1738  if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1739  if (!Process) MI_SET_PROCESS2("Kernel Section");
1743 
1745  if (!NT_SUCCESS(Status))
1746  {
1747  DPRINT1("Unable to create virtual mapping\n");
1748  KeBugCheck(MEMORY_MANAGEMENT);
1749  }
1750  ASSERT(MmIsPagePresent(Process, PAddress));
1751  if (Process)
1753 
1754  DPRINT("Address 0x%p\n", Address);
1755  return STATUS_SUCCESS;
1756  }
1757 
1760 
1761  /* The data must be paged in. Lock the file, so that the VDL doesn't get updated behind us. */
1762  FsRtlAcquireFileExclusive(Segment->FileObject);
1763 
1764  PFSRTL_COMMON_FCB_HEADER FcbHeader = Segment->FileObject->FsContext;
1765 
1767 
1768  FsRtlReleaseFile(Segment->FileObject);
1769 
1770  /* Lock address space again */
1772  if (!NT_SUCCESS(Status))
1773  {
1774  /* Damn */
1775  DPRINT1("Failed to page data in!\n");
1776  return STATUS_IN_PAGE_ERROR;
1777  }
1778 
1779  /* Everything went fine. Restart the operation */
1781  }
1782  else if (IS_SWAP_FROM_SSE(Entry))
1783  {
1784  SWAPENTRY SwapEntry;
1785 
1786  SwapEntry = SWAPENTRY_FROM_SSE(Entry);
1787 
1788  /* See if a page op is running on this segment. */
1789  if (SwapEntry == MM_WAIT_ENTRY)
1790  {
1793  YieldProcessor();
1796  }
1797 
1798  /*
1799  * Release all our locks and read in the page from disk
1800  */
1803 
1806  if (!NT_SUCCESS(Status))
1807  {
1808  KeBugCheck(MEMORY_MANAGEMENT);
1809  }
1810 
1811  Status = MmReadFromSwapPage(SwapEntry, Page);
1812  if (!NT_SUCCESS(Status))
1813  {
1814  KeBugCheck(MEMORY_MANAGEMENT);
1815  }
1816 
1817  /*
1818  * Relock the address space and segment
1819  */
1822 
1823  /*
1824  * Check the entry. No one should change the status of a page
1825  * that has a pending page-in.
1826  */
1828  if (Entry1 != MAKE_SWAP_SSE(MM_WAIT_ENTRY))
1829  {
1830  DPRINT1("Someone changed ppte entry while we slept (%x vs %x)\n", Entry, Entry1);
1831  KeBugCheck(MEMORY_MANAGEMENT);
1832  }
1833 
1834  /*
1835  * Save the swap entry.
1836  */
1837  MmSetSavedSwapEntryPage(Page, SwapEntry);
1838 
1839  /* Map the page into the process address space */
1841  PAddress,
1842  Attributes,
1843  Page);
1844  if (!NT_SUCCESS(Status))
1845  {
1846  DPRINT1("Unable to create virtual mapping\n");
1847  KeBugCheck(MEMORY_MANAGEMENT);
1848  }
1849  if (Process)
1851 
1852  /*
1853  * Mark the offset within the section as having valid, in-memory
1854  * data
1855  */
1856  Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
1859 
1860  DPRINT("Address 0x%p\n", Address);
1861  return STATUS_SUCCESS;
1862  }
1863  else
1864  {
1865  /* We already have a page on this section offset. Map it into the process address space. */
1866  Page = PFN_FROM_SSE(Entry);
1867 
1869  PAddress,
1870  Attributes,
1871  Page);
1872  if (!NT_SUCCESS(Status))
1873  {
1874  DPRINT1("Unable to create virtual mapping\n");
1875  KeBugCheck(MEMORY_MANAGEMENT);
1876  }
1877 
1878  if (Process)
1880 
1881  /* Take a reference on it */
1884 
1885  DPRINT("Address 0x%p\n", Address);
1886  return STATUS_SUCCESS;
1887  }
1888 }
1889 
1890 NTSTATUS
1891 NTAPI
1894  PVOID Address,
1895  BOOLEAN Locked)
1896 {
1898  PFN_NUMBER OldPage;
1899  PFN_NUMBER NewPage;
1900  PVOID PAddress;
1903  ULONG_PTR Entry;
1905  BOOLEAN Cow = FALSE;
1906  ULONG NewProtect;
1907 
1908  DPRINT("MmAccessFaultSectionView(%p, %p, %p)\n", AddressSpace, MemoryArea, Address);
1909 
1910  /* Get the region for this address */
1912  &MemoryArea->SectionData.RegionListHead,
1913  Address, NULL);
1914  ASSERT(Region != NULL);
1915  if (!(Region->Protect & PAGE_IS_WRITABLE))
1916  return STATUS_ACCESS_VIOLATION;
1917 
1918  /* Make sure we have a page mapping for this address. */
1920  {
1922  if (!NT_SUCCESS(Status))
1923  {
1924  /* This is invalid access ! */
1925  return Status;
1926  }
1927  }
1928 
1929  /*
1930  * Check if the page has already been set readwrite
1931  */
1933  {
1934  DPRINT("Address 0x%p\n", Address);
1935  return STATUS_SUCCESS;
1936  }
1937 
1938  /* Check if we are doing Copy-On-Write */
1939  Segment = MemoryArea->SectionData.Segment;
1940  Cow = Segment->WriteCopy || (Region->Protect & PAGE_IS_WRITECOPY);
1941 
1942  if (!Cow)
1943  {
1944  /* Simply update page protection and we're done */
1945  MmSetPageProtect(Process, Address, Region->Protect);
1946  return STATUS_SUCCESS;
1947  }
1948 
1949  /* Calculate the new protection & check if we should update the region */
1950  NewProtect = Region->Protect;
1952  {
1954  if (Region->Protect & PAGE_IS_EXECUTABLE)
1956  else
1959  &MemoryArea->SectionData.RegionListHead,
1962  }
1963 
1964  /*
1965  * Find the offset of the page
1966  */
1967  PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1968  Offset.QuadPart = (ULONG_PTR)PAddress - MA_GetStartingAddress(MemoryArea)
1969  + MemoryArea->SectionData.ViewOffset;
1970 
1971  /* Get the page mapping this section offset. */
1974 
1975  /* Get the current page mapping for the process */
1976  ASSERT(MmIsPagePresent(Process, PAddress));
1977  OldPage = MmGetPfnForProcess(Process, PAddress);
1978  ASSERT(OldPage != 0);
1979 
1980  if (IS_SWAP_FROM_SSE(Entry) ||
1981  PFN_FROM_SSE(Entry) != OldPage)
1982  {
1984  /* This is a private page. We must only change the page protection. */
1985  MmSetPageProtect(Process, PAddress, NewProtect);
1986  return STATUS_SUCCESS;
1987  }
1988 
1989  /*
1990  * Allocate a page
1991  */
1993  {
1994  KeBugCheck(MEMORY_MANAGEMENT);
1995  }
1996 
1997  /*
1998  * Copy the old page
1999  */
2000  NT_VERIFY(NT_SUCCESS(MiCopyFromUserPage(NewPage, PAddress)));
2001 
2002  /*
2003  * Unshare the old page.
2004  */
2005  DPRINT("Swapping page (Old %x New %x)\n", OldPage, NewPage);
2006  MmDeleteVirtualMapping(Process, PAddress, NULL, NULL);
2007  if (Process)
2008  MmDeleteRmap(OldPage, Process, PAddress);
2011 
2012  /*
2013  * Set the PTE to point to the new page
2014  */
2015  if (!NT_SUCCESS(MmCreateVirtualMapping(Process, PAddress, NewProtect, NewPage)))
2016  {
2017  DPRINT1("MmCreateVirtualMapping failed, unable to create virtual mapping, not out of memory\n");
2018  KeBugCheck(MEMORY_MANAGEMENT);
2019  }
2020 
2021  if (Process)
2022  MmInsertRmap(NewPage, Process, PAddress);
2023 
2024  DPRINT("Address 0x%p\n", Address);
2025  return STATUS_SUCCESS;
2026 }
2027 
2028 NTSTATUS
2029 NTAPI
2033  SIZE_T Length,
2034  ULONG Protect,
2035  PULONG OldProtect)
2036 {
2038  NTSTATUS Status;
2039  ULONG_PTR MaxLength;
2040 
2042  if (Length > MaxLength)
2043  Length = (ULONG)MaxLength;
2044 
2046  &MemoryArea->SectionData.RegionListHead,
2047  BaseAddress, NULL);
2048  ASSERT(Region != NULL);
2049 
2050  if ((MemoryArea->Flags & SEC_NO_CHANGE) &&
2051  Region->Protect != Protect)
2052  {
2054  }
2055 
2056  *OldProtect = Region->Protect;
2058  &MemoryArea->SectionData.RegionListHead,
2059  BaseAddress, Length, Region->Type, Protect,
2061 
2062  return Status;
2063 }
2064 
2067  PVOID Address,
2070 {
2072  PVOID RegionBaseAddress;
2074 
2076  &MemoryArea->SectionData.RegionListHead,
2077  Address, &RegionBaseAddress);
2078  if (Region == NULL)
2079  {
2080  return STATUS_UNSUCCESSFUL;
2081  }
2082 
2084  {
2085  Segment = MemoryArea->SectionData.Segment;
2086  Info->AllocationBase = (PUCHAR)MA_GetStartingAddress(MemoryArea) - Segment->Image.VirtualAddress;
2087  Info->Type = MEM_IMAGE;
2088  }
2089  else
2090  {
2091  Info->AllocationBase = (PVOID)MA_GetStartingAddress(MemoryArea);
2092  Info->Type = MEM_MAPPED;
2093  }
2094  Info->BaseAddress = RegionBaseAddress;
2095  Info->AllocationProtect = MmProtectToValue[MemoryArea->VadNode.u.VadFlags.Protection];
2096  Info->RegionSize = Region->Length;
2097  Info->State = MEM_COMMIT;
2098  Info->Protect = Region->Protect;
2099 
2101  return STATUS_SUCCESS;
2102 }
2103 
2104 VOID NTAPI
2106 {
2107  PSECTION Section = ObjectBody;
2108 
2109  /* Check if it's an ARM3, or ReactOS section */
2110  if (!MiIsRosSectionObject(Section))
2111  {
2112  MiDeleteARM3Section(ObjectBody);
2113  return;
2114  }
2115 
2116  DPRINT("MmpDeleteSection(ObjectBody %p)\n", ObjectBody);
2117  if (Section->u.Flags.Image)
2118  {
2119  PMM_IMAGE_SECTION_OBJECT ImageSectionObject = (PMM_IMAGE_SECTION_OBJECT)Section->Segment;
2120 
2121  /*
2122  * NOTE: Section->ImageSection can be NULL for short time
2123  * during the section creating. If we fail for some reason
2124  * until the image section is properly initialized we shouldn't
2125  * process further here.
2126  */
2127  if (Section->Segment == NULL)
2128  return;
2129 
2130  KIRQL OldIrql = MiAcquirePfnLock();
2131  ImageSectionObject->SectionCount--;
2132 
2133  /* We just dereference the first segment */
2134  ASSERT(ImageSectionObject->RefCount > 0);
2135  /* MmDereferenceSegmentWithLock releases PFN lock */
2136  MmDereferenceSegmentWithLock(ImageSectionObject->Segments, OldIrql);
2137  }
2138  else
2139  {
2141 
2142  /*
2143  * NOTE: Section->Segment can be NULL for short time
2144  * during the section creating.
2145  */
2146  if (Segment == NULL)
2147  return;
2148 
2149  KIRQL OldIrql = MiAcquirePfnLock();
2150  Segment->SectionCount--;
2151 
2152  /* MmDereferenceSegmentWithLock releases PFN lock */
2153  MmDereferenceSegmentWithLock(Segment, OldIrql);
2154  }
2155 }
2156 
2157 VOID NTAPI
2159  IN PVOID Object,
2162  IN ULONG SystemHandleCount)
2163 {
2164  DPRINT("MmpCloseSection(OB %p, HC %lu)\n", Object, ProcessHandleCount);
2165 }
2166 
2167 CODE_SEG("INIT")
2168 NTSTATUS
2169 NTAPI
2171 {
2172  PSECTION PhysSection;
2173  NTSTATUS Status;
2175  UNICODE_STRING Name = RTL_CONSTANT_STRING(L"\\Device\\PhysicalMemory");
2176  LARGE_INTEGER SectionSize;
2177  HANDLE Handle;
2179 
2180  /*
2181  * Create the section mapping physical memory
2182  */
2183  SectionSize.QuadPart = MmHighestPhysicalPage * PAGE_SIZE;
2185  &Name,
2187  NULL,
2188  NULL);
2189  /*
2190  * Create the Object
2191  */
2194  &Obj,
2196  NULL,
2197  sizeof(*PhysSection),
2198  0,
2199  0,
2200  (PVOID*)&PhysSection);
2201  if (!NT_SUCCESS(Status))
2202  {
2203  DPRINT1("MmCreatePhysicalMemorySection: failed to create object (0x%lx)\n", Status);
2204  return Status;
2205  }
2206 
2207  /*
2208  * Initialize it
2209  */
2210  RtlZeroMemory(PhysSection, sizeof(*PhysSection));
2211 
2212  /* Mark this as a "ROS Section" */
2213  PhysSection->u.Flags.filler = 1;
2214  PhysSection->InitialPageProtection = PAGE_EXECUTE_READWRITE;
2215  PhysSection->u.Flags.PhysicalMemory = 1;
2216  PhysSection->SizeOfSection = SectionSize;
2219  if (Segment == NULL)
2220  {
2221  ObDereferenceObject(PhysSection);
2222  return STATUS_NO_MEMORY;
2223  }
2225  PhysSection->Segment = (PSEGMENT)Segment;
2226  Segment->RefCount = 1;
2227 
2228  Segment->ReferenceCount = &Segment->RefCount;
2229  Segment->Flags = &Segment->SegFlags;
2230 
2232  Segment->Image.FileOffset = 0;
2233  Segment->Protection = PAGE_EXECUTE_READWRITE;
2234  Segment->RawLength = SectionSize;
2235  Segment->Length = SectionSize;
2236  Segment->SegFlags = MM_PHYSICALMEMORY_SEGMENT;
2237  Segment->WriteCopy = FALSE;
2238  Segment->Image.VirtualAddress = 0;
2239  Segment->Image.Characteristics = 0;
2241 
2242  Status = ObInsertObject(PhysSection,
2243  NULL,
2245  0,
2246  NULL,
2247  &Handle);
2248  if (!NT_SUCCESS(Status))
2249  {
2250  ObDereferenceObject(PhysSection);
2251  return Status;
2252  }
2254 
2255  return STATUS_SUCCESS;
2256 }
2257 
2258 CODE_SEG("INIT")
2259 NTSTATUS
2260 NTAPI
2262 {
2263  OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
2265 
2266  DPRINT("Creating Section Object Type\n");
2267 
2268  /* Initialize the section based root */
2271 
2272  /* Initialize the Section object type */
2273  RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
2274  RtlInitUnicodeString(&Name, L"Section");
2275  ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
2276  ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(SECTION);
2277  ObjectTypeInitializer.PoolType = PagedPool;
2278  ObjectTypeInitializer.UseDefaultObject = TRUE;
2279  ObjectTypeInitializer.GenericMapping = MmpSectionMapping;
2280  ObjectTypeInitializer.DeleteProcedure = MmpDeleteSection;
2281  ObjectTypeInitializer.CloseProcedure = MmpCloseSection;
2282  ObjectTypeInitializer.ValidAccessMask = SECTION_ALL_ACCESS;
2283  ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
2284  ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &MmSectionObjectType);
2285 
2287 
2288  return STATUS_SUCCESS;
2289 }
2290 
2291 static
2292 NTSTATUS
2293 NTAPI
2297  PLARGE_INTEGER UMaximumSize,
2301  BOOLEAN GotFileHandle)
2302 /*
2303  * Create a section backed by a data file
2304  */
2305 {
2306  PSECTION Section;
2307  NTSTATUS Status;
2310  KIRQL OldIrql;
2311 
2312  /*
2313  * Create the section
2314  */
2319  NULL,
2320  sizeof(*Section),
2321  0,
2322  0,
2323  (PVOID*)&Section);
2324  if (!NT_SUCCESS(Status))
2325  {
2326  return Status;
2327  }
2328  /*
2329  * Initialize it
2330  */
2331  RtlZeroMemory(Section, sizeof(*Section));
2332 
2333  /* Mark this as a "ROS" section */
2334  Section->u.Flags.filler = 1;
2335  Section->InitialPageProtection = SectionPageProtection;
2336  Section->u.Flags.File = 1;
2337 
2339  Section->u.Flags.NoChange = 1;
2341  Section->u.Flags.Reserve = 1;
2342 
2343  if (!GotFileHandle)
2344  {
2345  ASSERT(UMaximumSize != NULL);
2346  // ASSERT(UMaximumSize->QuadPart != 0);
2347  MaximumSize = *UMaximumSize;
2348  }
2349  else
2350  {
2353  if (!NT_SUCCESS(Status))
2354  {
2355  ObDereferenceObject(Section);
2356  return Status;
2357  }
2358 
2359  /*
2360  * FIXME: Revise this once a locking order for file size changes is
2361  * decided
2362  */
2363  if ((UMaximumSize != NULL) && (UMaximumSize->QuadPart != 0))
2364  {
2365  MaximumSize = *UMaximumSize;
2366  }
2367  else
2368  {
2370  /* Mapping zero-sized files isn't allowed. */
2371  if (MaximumSize.QuadPart == 0)
2372  {
2373  ObDereferenceObject(Section);
2375  }
2376  }
2377 
2378  if (MaximumSize.QuadPart > FileSize.QuadPart)
2379  {
2382  sizeof(LARGE_INTEGER),
2383  &MaximumSize);
2384  if (!NT_SUCCESS(Status))
2385  {
2386  ObDereferenceObject(Section);
2388  }
2389  }
2390  }
2391 
2392  if (FileObject->SectionObjectPointer == NULL)
2393  {
2394  ObDereferenceObject(Section);
2396  }
2397 
2398  /*
2399  * Lock the file
2400  */
2402  if (Status != STATUS_SUCCESS)
2403  {
2404  ObDereferenceObject(Section);
2405  return Status;
2406  }
2407 
2408  /* Lock the PFN lock while messing with Section Object pointers */
2409 grab_segment:
2410  OldIrql = MiAcquirePfnLock();
2411  Segment = FileObject->SectionObjectPointer->DataSectionObject;
2412 
2413  while (Segment && (Segment->SegFlags & (MM_SEGMENT_INDELETE | MM_SEGMENT_INCREATE)))
2414  {
2415  MiReleasePfnLock(OldIrql);
2417  OldIrql = MiAcquirePfnLock();
2418  Segment = FileObject->SectionObjectPointer->DataSectionObject;
2419  }
2420 
2421  /*
2422  * If this file hasn't been mapped as a data file before then allocate a
2423  * section segment to describe the data file mapping
2424  */
2425  if (Segment == NULL)
2426  {
2427  /* Release the lock. ExAllocatePoolWithTag might acquire it */
2428  MiReleasePfnLock(OldIrql);
2429 
2432  if (Segment == NULL)
2433  {
2434  //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
2435  ObDereferenceObject(Section);
2436  return STATUS_NO_MEMORY;
2437  }
2438 
2439  /* We are creating it */
2440  RtlZeroMemory(Segment, sizeof(*Segment));
2442  Segment->RefCount = 1;
2443 
2444  /* Acquire lock again */
2445  OldIrql = MiAcquirePfnLock();
2446 
2447  if (FileObject->SectionObjectPointer->DataSectionObject != NULL)
2448  {
2449  /* Well that's bad luck. Restart it all over */
2450  MiReleasePfnLock(OldIrql);
2452  goto grab_segment;
2453  }
2454 
2455  FileObject->SectionObjectPointer->DataSectionObject = Segment;
2456 
2457  /* We're safe to release the lock now */
2458  MiReleasePfnLock(OldIrql);
2459 
2460  Section->Segment = (PSEGMENT)Segment;
2461 
2462  /* Self-referencing segment */
2463  Segment->Flags = &Segment->SegFlags;
2464  Segment->ReferenceCount = &Segment->RefCount;
2465 
2466  Segment->SectionCount = 1;
2467 
2469  Segment->FileObject = FileObject;
2471 
2472  Segment->Image.FileOffset = 0;
2473  Segment->Protection = SectionPageProtection;
2474 
2475  Segment->Image.Characteristics = 0;
2478  {
2479  Segment->Length.QuadPart = Segment->RawLength.QuadPart = 0;
2480  }
2481  else
2482  {
2483  Segment->RawLength.QuadPart = MaximumSize.QuadPart;
2484  Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
2485  }
2486  Segment->Image.VirtualAddress = 0;
2488 
2489  /* We're good to use it now */
2490  OldIrql = MiAcquirePfnLock();
2491  Segment->SegFlags &= ~MM_SEGMENT_INCREATE;
2492  MiReleasePfnLock(OldIrql);
2493  }
2494  else
2495  {
2496  Section->Segment = (PSEGMENT)Segment;
2497  InterlockedIncrement64(&Segment->RefCount);
2498  InterlockedIncrementUL(&Segment->SectionCount);
2499 
2500  MiReleasePfnLock(OldIrql);
2501 
2503 
2504  if (MaximumSize.QuadPart > Segment->RawLength.QuadPart &&
2506  {
2507  Segment->RawLength.QuadPart = MaximumSize.QuadPart;
2508  Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
2509  }
2510 
2512  }
2513  Section->SizeOfSection = MaximumSize;
2514 
2515  //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
2516  *SectionObject = Section;
2517  return STATUS_SUCCESS;
2518 }
2519 
2520 /*
2521  TODO: not that great (declaring loaders statically, having to declare all of
2522  them, having to keep them extern, etc.), will fix in the future
2523 */
2525 (
2526  IN CONST VOID * FileHeader,
2527  IN SIZE_T FileHeaderSize,
2528  IN PVOID File,
2529  OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2530  OUT PULONG Flags,
2531  IN PEXEFMT_CB_READ_FILE ReadFileCb,
2532  IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
2533 );
2534 
2536 (
2537  IN CONST VOID * FileHeader,
2538  IN SIZE_T FileHeaderSize,
2539  IN PVOID File,
2540  OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2541  OUT PULONG Flags,
2542  IN PEXEFMT_CB_READ_FILE ReadFileCb,
2543  IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
2544 );
2545 
2547 {
2549 #ifdef __ELF
2551 #endif
2552 };
2553 
2554 static
2556 NTAPI
2558 {
2559  SIZE_T SizeOfSegments;
2560  PMM_SECTION_SEGMENT Segments;
2561 
2562  /* TODO: check for integer overflow */
2563  SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * NrSegments;
2564 
2566  SizeOfSegments,
2568 
2569  if(Segments)
2570  RtlZeroMemory(Segments, SizeOfSegments);
2571 
2572  return Segments;
2573 }
2574 static
2575 NTSTATUS
2576 NTAPI
2579  IN ULONG Length,
2580  OUT PVOID * Data,
2581  OUT PVOID * AllocBase,
2582  OUT PULONG ReadSize)
2583 {
2584  NTSTATUS Status;
2586  ULONG AdjustOffset;
2587  ULONG OffsetAdjustment;
2588  ULONG BufferSize;
2589  ULONG UsedSize;
2590  PVOID Buffer;
2593 
2595 
2596  if(Length == 0)
2597  {
2598  KeBugCheck(MEMORY_MANAGEMENT);
2599  }
2600 
2601  FileOffset = *Offset;
2602 
2603  /* Negative/special offset: it cannot be used in this context */
2604  if(FileOffset.u.HighPart < 0)
2605  {
2606  KeBugCheck(MEMORY_MANAGEMENT);
2607  }
2608 
2609  AdjustOffset = PAGE_ROUND_DOWN(FileOffset.u.LowPart);
2610  OffsetAdjustment = FileOffset.u.LowPart - AdjustOffset;
2611  FileOffset.u.LowPart = AdjustOffset;
2612 
2613  BufferSize = Length + OffsetAdjustment;
2615 
2616  /*
2617  * It's ok to use paged pool, because this is a temporary buffer only used in
2618  * the loading of executables. The assumption is that MmCreateSection is
2619  * always called at low IRQLs and that these buffers don't survive a brief
2620  * initialization phase
2621  */
2623  if (!Buffer)
2624  {
2626  }
2627 
2629 
2630  UsedSize = (ULONG)Iosb.Information;
2631 
2632  if(NT_SUCCESS(Status) && UsedSize < OffsetAdjustment)
2633  {
2636  }
2637 
2638  if(NT_SUCCESS(Status))
2639  {
2640  *Data = (PVOID)((ULONG_PTR)Buffer + OffsetAdjustment);
2641  *AllocBase = Buffer;
2642  *ReadSize = UsedSize - OffsetAdjustment;
2643  }
2644  else
2645  {
2646  ExFreePoolWithTag(Buffer, 'rXmM');
2647  }
2648 
2649  return Status;
2650 }
2651 
2652 #ifdef NASSERT
2653 # define MmspAssertSegmentsSorted(OBJ_) ((void)0)
2654 # define MmspAssertSegmentsNoOverlap(OBJ_) ((void)0)
2655 # define MmspAssertSegmentsPageAligned(OBJ_) ((void)0)
2656 #else
2657 static
2658 VOID
2659 NTAPI
2661 {
2662  ULONG i;
2663 
2664  for( i = 1; i < ImageSectionObject->NrSegments; ++ i )
2665  {
2666  ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >=
2667  ImageSectionObject->Segments[i - 1].Image.VirtualAddress);
2668  }
2669 }
2670 
2671 static
2672 VOID
2673 NTAPI
2675 {
2676  ULONG i;
2677 
2678  MmspAssertSegmentsSorted(ImageSectionObject);
2679 
2680  for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2681  {
2682  ASSERT(ImageSectionObject->Segments[i].Length.QuadPart > 0);
2683 
2684  if(i > 0)
2685  {
2686  ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >=
2687  (ImageSectionObject->Segments[i - 1].Image.VirtualAddress +
2688  ImageSectionObject->Segments[i - 1].Length.QuadPart));
2689  }
2690  }
2691 }
2692 
2693 static
2694 VOID
2695 NTAPI
2697 {
2698  ULONG i;
2699 
2700  for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2701  {
2702  ASSERT((ImageSectionObject->Segments[i].Image.VirtualAddress % PAGE_SIZE) == 0);
2703  ASSERT((ImageSectionObject->Segments[i].Length.QuadPart % PAGE_SIZE) == 0);
2704  }
2705 }
2706 #endif
2707 
2708 static
2709 int
2710 __cdecl
2711 MmspCompareSegments(const void * x,
2712  const void * y)
2713 {
2714  const MM_SECTION_SEGMENT *Segment1 = (const MM_SECTION_SEGMENT *)x;
2715  const MM_SECTION_SEGMENT *Segment2 = (const MM_SECTION_SEGMENT *)y;
2716 
2717  if (Segment1->Image.VirtualAddress > Segment2->Image.VirtualAddress)
2718  return 1;
2719  else if (Segment1->Image.VirtualAddress < Segment2->Image.VirtualAddress)
2720  return -1;
2721  else
2722  return 0;
2723 }
2724 
2725 /*
2726  * Ensures an image section's segments are sorted in memory
2727  */
2728 static
2729 VOID
2730 NTAPI
2732  IN ULONG Flags)
2733 {
2735  {
2736  MmspAssertSegmentsSorted(ImageSectionObject);
2737  }
2738  else
2739  {
2740  qsort(ImageSectionObject->Segments,
2741  ImageSectionObject->NrSegments,
2742  sizeof(ImageSectionObject->Segments[0]),
2744  }
2745 }
2746 
2747 
2748 /*
2749  * Ensures an image section's segments don't overlap in memory and don't have
2750  * gaps and don't have a null size. We let them map to overlapping file regions,
2751  * though - that's not necessarily an error
2752  */
2753 static
2754 BOOLEAN
2755 NTAPI
2758  IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2759  IN ULONG Flags
2760 )
2761 {
2762  ULONG i;
2763 
2765  {
2766  MmspAssertSegmentsNoOverlap(ImageSectionObject);
2767  return TRUE;
2768  }
2769 
2770  ASSERT(ImageSectionObject->NrSegments >= 1);
2771 
2772  for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2773  {
2774  if(ImageSectionObject->Segments[i].Length.QuadPart == 0)
2775  {
2776  return FALSE;
2777  }
2778 
2779  if(i > 0)
2780  {
2781  /*
2782  * TODO: relax the limitation on gaps. For example, gaps smaller than a
2783  * page could be OK (Windows seems to be OK with them), and larger gaps
2784  * could lead to image sections spanning several discontiguous regions
2785  * (NtMapViewOfSection could then refuse to map them, and they could
2786  * e.g. only be allowed as parameters to NtCreateProcess, like on UNIX)
2787  */
2788  if ((ImageSectionObject->Segments[i - 1].Image.VirtualAddress +
2789  ImageSectionObject->Segments[i - 1].Length.QuadPart) !=
2790  ImageSectionObject->Segments[i].Image.VirtualAddress)
2791  {
2792  return FALSE;
2793  }
2794  }
2795  }
2796 
2797  return TRUE;
2798 }
2799 
2800 /*
2801  * Merges and pads an image section's segments until they all are page-aligned
2802  * and have a size that is a multiple of the page size
2803  */
2804 static
2805 BOOLEAN
2806 NTAPI
2809  IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2810  IN ULONG Flags
2811 )
2812 {
2813  ULONG i;
2814  ULONG LastSegment;
2815  PMM_SECTION_SEGMENT EffectiveSegment;
2816 
2818  {
2819  MmspAssertSegmentsPageAligned(ImageSectionObject);
2820  return TRUE;
2821  }
2822 
2823  LastSegment = 0;
2824  EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
2825 
2826  for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2827  {
2828  /*
2829  * The first segment requires special handling
2830  */
2831  if (i == 0)
2832  {
2834  ULONG_PTR VirtualOffset;
2835 
2836  VirtualAddress = EffectiveSegment->Image.VirtualAddress;
2837 
2838  /* Round down the virtual address to the nearest page */
2839  EffectiveSegment->Image.VirtualAddress = PAGE_ROUND_DOWN(VirtualAddress);
2840 
2841  /* Round up the virtual size to the nearest page */
2842  EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(VirtualAddress + EffectiveSegment->Length.QuadPart) -
2843  EffectiveSegment->Image.VirtualAddress;
2844 
2845  /* Adjust the raw address and size */
2846  VirtualOffset = VirtualAddress - EffectiveSegment->Image.VirtualAddress;
2847 
2848  if (EffectiveSegment->Image.FileOffset < VirtualOffset)
2849  {
2850  return FALSE;
2851  }
2852 
2853  /*
2854  * Garbage in, garbage out: unaligned base addresses make the file
2855  * offset point in curious and odd places, but that's what we were
2856  * asked for
2857  */
2858  EffectiveSegment->Image.FileOffset -= VirtualOffset;
2859  EffectiveSegment->RawLength.QuadPart += VirtualOffset;
2860  }
2861  else
2862  {
2863  PMM_SECTION_SEGMENT Segment = &ImageSectionObject->Segments[i];
2864  ULONG_PTR EndOfEffectiveSegment;
2865 
2866  EndOfEffectiveSegment = (ULONG_PTR)(EffectiveSegment->Image.VirtualAddress + EffectiveSegment->Length.QuadPart);
2867  ASSERT((EndOfEffectiveSegment % PAGE_SIZE) == 0);
2868 
2869  /*
2870  * The current segment begins exactly where the current effective
2871  * segment ended, therefore beginning a new effective segment
2872  */
2873  if (EndOfEffectiveSegment == Segment->Image.VirtualAddress)
2874  {
2875  LastSegment ++;
2876  ASSERT(LastSegment <= i);
2877  ASSERT(LastSegment < ImageSectionObject->NrSegments);
2878 
2879  EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
2880 
2881  if (LastSegment != i)
2882  {
2883  /*
2884  * Copy the current segment. If necessary, the effective segment
2885  * will be expanded later
2886  */
2887  *EffectiveSegment = *Segment;
2888  }
2889 
2890  /*
2891  * Page-align the virtual size. We know for sure the virtual address
2892  * already is
2893  */
2894  ASSERT((EffectiveSegment->Image.VirtualAddress % PAGE_SIZE) == 0);
2895  EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(EffectiveSegment->Length.QuadPart);
2896  }
2897  /*
2898  * The current segment is still part of the current effective segment:
2899  * extend the effective segment to reflect this
2900  */
2901  else if (EndOfEffectiveSegment > Segment->Image.VirtualAddress)
2902  {
2903  static const ULONG FlagsToProtection[16] =
2904  {
2905  PAGE_NOACCESS,
2906  PAGE_READONLY,
2921  };
2922 
2923  unsigned ProtectionFlags;
2924 
2925  /*
2926  * Extend the file size
2927  */
2928 
2929  /* Unaligned segments must be contiguous within the file */
2930  if (Segment->Image.FileOffset != (EffectiveSegment->Image.FileOffset +
2931  EffectiveSegment->RawLength.QuadPart))
2932  {
2933  return FALSE;
2934  }
2935 
2936  EffectiveSegment->RawLength.QuadPart += Segment->RawLength.QuadPart;
2937 
2938  /*
2939  * Extend the virtual size
2940  */
2941  ASSERT(PAGE_ROUND_UP(Segment->Image.VirtualAddress + Segment->Length.QuadPart) >= EndOfEffectiveSegment);
2942 
2943  EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(Segment->Image.VirtualAddress + Segment->Length.QuadPart) -
2944  EffectiveSegment->Image.VirtualAddress;
2945 
2946  /*
2947  * Merge the protection
2948  */
2949  EffectiveSegment->Protection |= Segment->Protection;
2950 
2951  /* Clean up redundance */
2952  ProtectionFlags = 0;
2953 
2954  if(EffectiveSegment->Protection & PAGE_IS_READABLE)
2955  ProtectionFlags |= 1 << 0;
2956 
2957  if(EffectiveSegment->Protection & PAGE_IS_WRITABLE)
2958  ProtectionFlags |= 1 << 1;
2959 
2960  if(EffectiveSegment->Protection & PAGE_IS_EXECUTABLE)
2961  ProtectionFlags |= 1 << 2;
2962 
2963  if(EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
2964  ProtectionFlags |= 1 << 3;
2965 
2966  ASSERT(ProtectionFlags < 16);
2967  EffectiveSegment->Protection = FlagsToProtection[ProtectionFlags];
2968 
2969  /* If a segment was required to be shared and cannot, fail */
2970  if(!(Segment->Protection & PAGE_IS_WRITECOPY) &&
2971  EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
2972  {
2973  return FALSE;
2974  }
2975  }
2976  /*
2977  * We assume no holes between segments at this point
2978  */
2979  else
2980  {
2981  KeBugCheck(MEMORY_MANAGEMENT);
2982  }
2983  }
2984  }
2985  ImageSectionObject->NrSegments = LastSegment + 1;
2986 
2987  return TRUE;
2988 }
2989 
2990 NTSTATUS
2992  PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
2993 {
2995  PVOID FileHeader;
2996  PVOID FileHeaderBuffer;
2997  ULONG FileHeaderSize;
2998  ULONG Flags;
2999  ULONG OldNrSegments;
3000  NTSTATUS Status;
3001  ULONG i;
3002 
3003  /*
3004  * Read the beginning of the file (2 pages). Should be enough to contain
3005  * all (or most) of the headers
3006  */
3007  Offset.QuadPart = 0;
3008 
3010  &Offset,
3011  PAGE_SIZE * 2,
3012  &FileHeader,
3013  &FileHeaderBuffer,
3014  &FileHeaderSize);
3015 
3016  if (!NT_SUCCESS(Status))
3017  return Status;
3018 
3019  if (FileHeaderSize == 0)
3020  {
3021  ExFreePool(FileHeaderBuffer);
3022  return STATUS_UNSUCCESSFUL;
3023  }
3024 
3025  /*
3026  * Look for a loader that can handle this executable
3027  */
3028  for (i = 0; i < RTL_NUMBER_OF(ExeFmtpLoaders); ++ i)
3029  {
3030  Flags = 0;
3031 
3032  Status = ExeFmtpLoaders[i](FileHeader,
3033  FileHeaderSize,
3034  FileObject,
3035  ImageSectionObject,
3036  &Flags,
3039 
3040  if (!NT_SUCCESS(Status))
3041  {
3042  if (ImageSectionObject->Segments)
3043  {
3044  ExFreePool(ImageSectionObject->Segments);
3045  ImageSectionObject->Segments = NULL;
3046  }
3047  }
3048 
3050  break;
3051  }
3052 
3053  ExFreePoolWithTag(FileHeaderBuffer, 'rXmM');
3054 
3055  /*
3056  * No loader handled the format
3057  */
3059  {
3062  }
3063 
3064  if (!NT_SUCCESS(Status))
3065  return Status;
3066 
3067  ASSERT(ImageSectionObject->Segments != NULL);
3068  ASSERT(ImageSectionObject->RefCount > 0);
3069 
3070  /*
3071  * Some defaults
3072  */
3073  /* FIXME? are these values platform-dependent? */
3074  if (ImageSectionObject->ImageInformation.MaximumStackSize == 0)
3075  ImageSectionObject->ImageInformation.MaximumStackSize = 0x40000;
3076 
3077  if(ImageSectionObject->ImageInformation.CommittedStackSize == 0)
3078  ImageSectionObject->ImageInformation.CommittedStackSize = 0x1000;
3079 
3080  if(ImageSectionObject->BasedAddress == NULL)
3081  {
3082  if(ImageSectionObject->ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL)
3083  ImageSectionObject->BasedAddress = (PVOID)0x10000000;
3084  else
3085  ImageSectionObject->BasedAddress = (PVOID)0x00400000;
3086  }
3087 
3088  /*
3089  * And now the fun part: fixing the segments
3090  */
3091 
3092  /* Sort them by virtual address */
3093  MmspSortSegments(ImageSectionObject, Flags);
3094 
3095  /* Ensure they don't overlap in memory */
3096  if (!MmspCheckSegmentBounds(ImageSectionObject, Flags))
3098 
3099  /* Ensure they are aligned */
3100  OldNrSegments = ImageSectionObject->NrSegments;
3101 
3102  if (!MmspPageAlignSegments(ImageSectionObject, Flags))
3104 
3105  /* Trim them if the alignment phase merged some of them */
3106  if (ImageSectionObject->NrSegments < OldNrSegments)
3107  {
3108  PMM_SECTION_SEGMENT Segments;
3109  SIZE_T SizeOfSegments;
3110 
3111  SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * ImageSectionObject->NrSegments;
3112 
3113  Segments = ExAllocatePoolWithTag(PagedPool,
3114  SizeOfSegments,
3116 
3117  if (Segments == NULL)
3119 
3120  RtlCopyMemory(Segments, ImageSectionObject->Segments, SizeOfSegments);
3121  ExFreePool(ImageSectionObject->Segments);
3122  ImageSectionObject->Segments = Segments;
3123  }
3124 
3125  /* And finish their initialization */
3126  for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3127  {
3128  ExInitializeFastMutex(&ImageSectionObject->Segments[i].Lock);
3129  ImageSectionObject->Segments[i].ReferenceCount = &ImageSectionObject->RefCount;
3130  ImageSectionObject->Segments[i].Flags = &ImageSectionObject->SegFlags;
3131  MiInitializeSectionPageTable(&ImageSectionObject->Segments[i]);
3132  ImageSectionObject->Segments[i].FileObject = FileObject;
3133  }
3134 
3135  ASSERT(ImageSectionObject->RefCount > 0);
3136 
3137  ImageSectionObject->FileObject = FileObject;
3138 
3140  return Status;
3141 }
3142 
3143 NTSTATUS
3147  PLARGE_INTEGER UMaximumSize,
3151 {
3152  PSECTION Section;
3153  NTSTATUS Status;
3154  PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3155  KIRQL OldIrql;
3156 
3157 
3158  if (FileObject == NULL)
3160 
3161  if (FileObject->SectionObjectPointer == NULL)
3162  {
3163  DPRINT1("Denying section creation due to missing cache initialization\n");
3165  }
3166 
3167  /*
3168  * Create the section
3169  */
3174  NULL,
3175  sizeof(*Section),
3176  0,
3177  0,
3178  (PVOID*)(PVOID)&Section);
3179  if (!NT_SUCCESS(Status))
3180  {
3181  return Status;
3182  }
3183 
3184  /*
3185  * Initialize it
3186  */
3187  RtlZeroMemory(Section, sizeof(*Section));
3188 
3189  /* Mark this as a "ROS" Section */
3190  Section->u.Flags.filler = 1;
3191 
3192  Section->InitialPageProtection = SectionPageProtection;
3193  Section->u.Flags.File = 1;
3194  Section->u.Flags.Image = 1;
3196  Section->u.Flags.NoChange = 1;
3197 
3198 grab_image_section_object:
3199  OldIrql = MiAcquirePfnLock();
3200 
3201  /* Wait for it to be properly created or deleted */
3202  ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
3203  while(ImageSectionObject && (ImageSectionObject->SegFlags & (MM_SEGMENT_INDELETE | MM_SEGMENT_INCREATE)))
3204  {
3205  MiReleasePfnLock(OldIrql);
3206 
3208 
3209  OldIrql = MiAcquirePfnLock();
3210  ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
3211  }
3212 
3213  if (ImageSectionObject == NULL)
3214  {
3215  NTSTATUS StatusExeFmt;
3216 
3217  /* Release the lock because ExAllocatePoolWithTag could need to acquire it */
3218  MiReleasePfnLock(OldIrql);
3219 
3220  ImageSectionObject = ExAllocatePoolZero(NonPagedPool, sizeof(MM_IMAGE_SECTION_OBJECT), TAG_MM_SECTION_SEGMENT);
3221  if (ImageSectionObject == NULL)
3222  {
3223  ObDereferenceObject(Section);
3224  return STATUS_NO_MEMORY;
3225  }
3226 
3227  ImageSectionObject->SegFlags = MM_SEGMENT_INCREATE;
3228  ImageSectionObject->RefCount = 1;
3229  ImageSectionObject->SectionCount = 1;
3230 
3231  OldIrql = MiAcquirePfnLock();
3232  if (FileObject->SectionObjectPointer->ImageSectionObject != NULL)
3233  {
3234  MiReleasePfnLock(OldIrql);
3235  /* Bad luck. Start over */
3236  ExFreePoolWithTag(ImageSectionObject, TAG_MM_SECTION_SEGMENT);
3237  goto grab_image_section_object;
3238  }
3239 
3240  FileObject->SectionObjectPointer->ImageSectionObject = ImageSectionObject;
3241 
3242  MiReleasePfnLock(OldIrql);
3243 
3244  /* Purge the cache */
3245  CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, NULL);
3246 
3247  StatusExeFmt = ExeFmtpCreateImageSection(FileObject, ImageSectionObject);
3248 
3249  if (!NT_SUCCESS(StatusExeFmt))
3250  {
3251  /* Unset */
3252  OldIrql = MiAcquirePfnLock();
3253  FileObject->SectionObjectPointer->ImageSectionObject = NULL;
3254  MiReleasePfnLock(OldIrql);
3255 
3256  if(ImageSectionObject->Segments != NULL)
3257  ExFreePool(ImageSectionObject->Segments);
3258 
3259  /*
3260  * If image file is empty, then return that the file is invalid for section
3261  */
3262  Status = StatusExeFmt;
3263  if (StatusExeFmt == STATUS_END_OF_FILE)
3264  {
3266  }
3267 
3268  ExFreePoolWithTag(ImageSectionObject, TAG_MM_SECTION_SEGMENT);
3269  ObDereferenceObject(Section);
3270  return Status;
3271  }
3272 
3273  Section->Segment = (PSEGMENT)ImageSectionObject;
3274  ASSERT(ImageSectionObject->Segments);
3275  ASSERT(ImageSectionObject->RefCount > 0);
3276 
3277  /*
3278  * Lock the file
3279  */
3281  if (!NT_SUCCESS(Status))
3282  {
3283  /* Unset */
3284  OldIrql = MiAcquirePfnLock();
3285  FileObject->SectionObjectPointer->ImageSectionObject = NULL;
3286  MiReleasePfnLock(OldIrql);
3287 
3288  ExFreePool(ImageSectionObject->Segments);
3289  ExFreePool(ImageSectionObject);
3290  ObDereferenceObject(Section);
3291  return Status;
3292  }
3293 
3294  OldIrql = MiAcquirePfnLock();
3295  ImageSectionObject->SegFlags &= ~MM_SEGMENT_INCREATE;
3296 
3297  /* Take a ref on the file on behalf of the newly created structure */
3299 
3300  MiReleasePfnLock(OldIrql);
3301 
3302  Status = StatusExeFmt;
3303  }
3304  else
3305  {
3306  /* If FS driver called for delete, tell them it's not possible anymore. */
3307  ImageSectionObject->SegFlags &= ~MM_IMAGE_SECTION_FLUSH_DELETE;
3308 
3309  /* Take one ref */
3310  InterlockedIncrement64(&ImageSectionObject->RefCount);
3311  ImageSectionObject->SectionCount++;
3312 
3313  MiReleasePfnLock(OldIrql);
3314 
3315  Section->Segment = (PSEGMENT)ImageSectionObject;
3316 
3318  }
3319  //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
3320  *SectionObject = Section;
3321  ASSERT(ImageSectionObject->RefCount > 0);
3322 
3323  return Status;
3324 }
3325 
3326 
3327 
3328 static NTSTATUS
3331  BOOLEAN AsImage,
3333  PVOID* BaseAddress,
3334  SIZE_T ViewSize,
3335  ULONG Protect,
3336  LONGLONG ViewOffset,
3338 {
3339  PMEMORY_AREA MArea;
3340  NTSTATUS Status;
3341  ULONG Granularity;
3342 
3343  ASSERT(ViewSize != 0);
3344 
3345  if (Segment->WriteCopy)
3346  {
3347  /* We have to do this because the not present fault
3348  * and access fault handlers depend on the protection
3349  * that should be granted AFTER the COW fault takes
3350  * place to be in Region->Protect. The not present fault
3351  * handler changes this to the correct protection for COW when
3352  * mapping the pages into the process's address space. If a COW
3353  * fault takes place, the access fault handler sets the page protection
3354  * to these values for the newly copied pages
3355  */
3356  if (Protect == PAGE_WRITECOPY)
3358  else if (Protect == PAGE_EXECUTE_WRITECOPY)
3360  }
3361 
3362  if (*BaseAddress == NULL)
3363  Granularity = MM_ALLOCATION_GRANULARITY;
3364  else
3365  Granularity = PAGE_SIZE;
3366 
3367 #ifdef NEWCC
3368  if (Segment->Flags & MM_DATAFILE_SEGMENT)
3369  {
3371  FileOffset.QuadPart = ViewOffset;
3372  ObReferenceObject(Section);
3374  }
3375 #endif
3378  BaseAddress,
3379  ViewSize,
3380  Protect,
3381  &MArea,
3383  Granularity);
3384  if (!NT_SUCCESS(Status))
3385  {
3386  DPRINT1("Mapping between 0x%p and 0x%p failed (%X).\n",
3387  (*BaseAddress), (char*)(*BaseAddress) + ViewSize, Status);
3388  return Status;
3389  }
3390 
3391  InterlockedIncrement64(Segment->ReferenceCount);
3392 
3393  MArea->SectionData.Segment = Segment;
3394  MArea->SectionData.ViewOffset = ViewOffset;
3395  if (AsImage)
3396  {
3398  }
3399 
3400  MmInitializeRegion(&MArea->SectionData.RegionListHead,
3401  ViewSize, 0, Protect);
3402 
3403  return STATUS_SUCCESS;
3404 }
3405 
3406 
3407 static VOID
3409  PFN_NUMBER Page, SWAPENTRY SwapEntry, BOOLEAN Dirty)
3410 {
3411  ULONG_PTR Entry;
3413  SWAPENTRY SavedSwapEntry;
3417 
3420 
3422 
3424  MemoryArea->SectionData.ViewOffset;
3425 
3426  Segment = MemoryArea->SectionData.Segment;
3427 
3429  while (Entry && MM_IS_WAIT_PTE(Entry))
3430  {
3433 
3434  YieldProcessor();
3435 
3439  }
3440 
3441  /*
3442  * For a dirty, datafile, non-private page, there shoulkd be no swap entry
3443  */
3444  if (*Segment->Flags & MM_DATAFILE_SEGMENT)
3445  {
3446  if (Page == PFN_FROM_SSE(Entry) && Dirty)
3447  {
3448  ASSERT(SwapEntry == 0);
3449  }
3450  }
3451 
3452  if (SwapEntry != 0)
3453  {
3454  /*
3455  * Sanity check
3456  */
3457  MmFreeSwapPage(SwapEntry);
3458  }
3459  else if (Page != 0)
3460  {
3461  if (IS_SWAP_FROM_SSE(Entry) ||
3462  Page != PFN_FROM_SSE(Entry))
3463  {
3464  ASSERT(Process != NULL);
3465 
3466  /*
3467  * Just dereference private pages
3468  */
3469  SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
3470  if (SavedSwapEntry != 0)
3471  {
3472  MmFreeSwapPage(SavedSwapEntry);
3474  }
3477  }
3478  else
3479  {
3480  if (Process)
3481  {
3483  }
3484 
3485  /* We don't dirtify for System Space Maps. We let Cc manage that */
3487  }
3488  }
3489 }
3490 
3491 static NTSTATUS
3494 {
3495  NTSTATUS Status;
3498  PLIST_ENTRY CurrentEntry;
3499  PMM_REGION CurrentRegion;
3500  PLIST_ENTRY RegionListHead;
3501 
3503  BaseAddress);
3504  if (MemoryArea == NULL)
3505  {
3506  return STATUS_UNSUCCESSFUL;
3507  }
3508 
3509  Segment = MemoryArea->SectionData.Segment;
3510 
3511 #ifdef NEWCC
3512  if (Segment->Flags & MM_DATAFILE_SEGMENT)
3513  {
3517 
3518  return Status;
3519  }
3520 #endif
3521 
3523 
3525 
3526  RegionListHead = &MemoryArea->SectionData.RegionListHead;
3527  while (!IsListEmpty(RegionListHead))
3528  {
3529  CurrentEntry = RemoveHeadList(RegionListHead);
3530  CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION, RegionListEntry);
3531  ExFreePoolWithTag(CurrentRegion, TAG_MM_REGION);
3532  }
3533 
3534  if ((*Segment->Flags) & MM_PHYSICALMEMORY_SEGMENT)
3535  {
3537  MemoryArea,
3538  NULL,
3539  NULL);
3540  }
3541  else
3542  {
3544  MemoryArea,
3546  AddressSpace);
3547  }
3549  MmDereferenceSegment(Segment);
3550  return Status;
3551 }
3552 
3553 /* This functions must be called with a locked address space */
3554 NTSTATUS
3555 NTAPI
3558  IN BOOLEAN SkipDebuggerNotify)
3559 {
3560  NTSTATUS Status;
3563  PVOID ImageBaseAddress = 0;
3564 
3565  DPRINT("Opening memory area Process %p BaseAddress %p\n",
3566  Process, BaseAddress);
3567 
3568  ASSERT(Process);
3569 
3571 
3573  BaseAddress);
3574  if (MemoryArea == NULL ||
3575 #ifdef NEWCC
3576  ((MemoryArea->Type != MEMORY_AREA_SECTION_VIEW) && (MemoryArea->Type != MEMORY_AREA_CACHE)) ||
3577 #else
3579 #endif
3581 
3582  {
3584 
3585  DPRINT1("Unable to find memory area at address %p.\n", BaseAddress);
3586  return STATUS_NOT_MAPPED_VIEW;
3587  }
3588 
3590  {
3591  ULONG i;
3592  ULONG NrSegments;
3593  PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3594  PMM_SECTION_SEGMENT SectionSegments;
3596 
3597  Segment = MemoryArea->SectionData.Segment;
3598  ImageSectionObject = ImageSectionObjectFromSegment(Segment);
3599  SectionSegments = ImageSectionObject->Segments;
3600  NrSegments = ImageSectionObject->NrSegments;
3601 
3603 
3604  /* Search for the current segment within the section segments
3605  * and calculate the image base address */
3606  for (i = 0; i < NrSegments; i++)
3607  {
3608  if (Segment == &SectionSegments[i])
3609  {
3610  ImageBaseAddress = (char*)BaseAddress - (ULONG_PTR)SectionSegments[i].Image.VirtualAddress;
3611  break;
3612  }
3613  }
3614  if (i >= NrSegments)
3615  {
3616  KeBugCheck(MEMORY_MANAGEMENT);
3617  }
3618 
3619  for (i = 0; i < NrSegments; i++)
3620  {
3621  PVOID SBaseAddress = (PVOID)
3622  ((char*)ImageBaseAddress + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
3623 
3624  Status = MmUnmapViewOfSegment(AddressSpace, SBaseAddress);
3625  if (!NT_SUCCESS(Status))
3626  {
3627  DPRINT1("MmUnmapViewOfSegment failed for %p (Process %p) with %lx\n",
3628  SBaseAddress, Process, Status);
3630  }
3631  }
3632  DPRINT("One mapping less for %p\n", ImageSectionObject->FileObject->SectionObjectPointer);
3633  InterlockedDecrement(&ImageSectionObject->MapCount);
3634  }
3635  else
3636  {
3638  if (!NT_SUCCESS(Status))
3639  {
3640  DPRINT1("MmUnmapViewOfSegment failed for %p (Process %p) with %lx\n",
3643  }
3644  }
3645 
3646  /* Notify debugger */
3647  if (ImageBaseAddress && !SkipDebuggerNotify) DbgkUnMapViewOfSection(ImageBaseAddress);
3648 
3649  return STATUS_SUCCESS;
3650 }
3651 
3652 
3653 
3654 
3677 NTSTATUS
3678 NTAPI
3680  _In_ HANDLE SectionHandle,
3681  _In_ SECTION_INFORMATION_CLASS SectionInformationClass,
3682  _Out_ PVOID SectionInformation,
3683  _In_ SIZE_T SectionInformationLength,
3685 {
3686  PSECTION Section;
3688  NTSTATUS Status;
3689  PAGED_CODE();
3690 
3692  if (PreviousMode != KernelMode)
3693  {
3694  _SEH2_TRY
3695  {
3696  ProbeForWrite(SectionInformation,
3697  SectionInformationLength,
3698  __alignof(ULONG));
3699  if (ResultLength != NULL)
3700  {
3702  sizeof(*ResultLength),
3703  __alignof(SIZE_T));
3704  }
3705  }
3707  {
3709  }
3710  _SEH2_END;
3711  }
3712 
3713  if (SectionInformationClass == SectionBasicInformation)
3714  {
3715  if (SectionInformationLength < sizeof(SECTION_BASIC_INFORMATION))
3716  {
3718  }
3719  }
3720  else if (SectionInformationClass == SectionImageInformation)
3721  {
3722  if (SectionInformationLength < sizeof(SECTION_IMAGE_INFORMATION))
3723  {
3725  }
3726  }
3727  else
3728  {
3730  }
3731 
3732  Status = ObReferenceObjectByHandle(SectionHandle,
3733  SECTION_QUERY,
3735  PreviousMode,
3736  (PVOID*)(PVOID)&Section,
3737  NULL);
3738  if (!NT_SUCCESS(Status))
3739  {
3740  DPRINT1("Failed to reference section: 0x%lx\n", Status);
3741  return Status;
3742  }
3743 
3744  switch(SectionInformationClass)
3745  {
3747  {
3749 
3750  Sbi.Size = Section->SizeOfSection;
3751  Sbi.BaseAddress = (PVOID)Section->Address.StartingVpn;
3752 
3753  Sbi.Attributes = 0;
3754  if (Section->u.Flags.File)
3755  Sbi.Attributes |= SEC_FILE;
3756  if (Section->u.Flags.Image)
3757  Sbi.Attributes |= SEC_IMAGE;
3758 
3759  /* Those are not set *************
3760  if (Section->u.Flags.Commit)
3761  Sbi.Attributes |= SEC_COMMIT;
3762  if (Section->u.Flags.Reserve)
3763  Sbi.Attributes |= SEC_RESERVE;
3764  **********************************/
3765 
3766  if (Section->u.Flags.Image)
3767  {
3768  if (MiIsRosSectionObject(Section))
3769  {
3770  PMM_IMAGE_SECTION_OBJECT ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
3771  Sbi.BaseAddress = 0;
3772  Sbi.Size.QuadPart = ImageSectionObject->ImageInformation.ImageFileSize;
3773  }
3774  else
3775  {
3776  /* Not supported yet */
3777  ASSERT(FALSE);
3778  }
3779  }
3780  else if (MiIsRosSectionObject(Section))
3781  {
3782  Sbi.BaseAddress = (PVOID)((PMM_SECTION_SEGMENT)Section->Segment)->Image.VirtualAddress;
3783  Sbi.Size.QuadPart = ((PMM_SECTION_SEGMENT)Section->Segment)->RawLength.QuadPart;
3784  }
3785  else
3786  {
3787  DPRINT1("Unimplemented code path!");
3788  }
3789 
3790  _SEH2_TRY
3791  {
3792  *((SECTION_BASIC_INFORMATION*)SectionInformation) = Sbi;
3793  if (ResultLength != NULL)
3794  {
3795  *ResultLength = sizeof(Sbi);
3796  }
3797  }
3799  {
3801  }
3802  _SEH2_END;
3803  break;
3804  }
3806  {
3807  if (!Section->u.Flags.Image)
3808  {
3810  }
3811  else if (MiIsRosSectionObject(Section))
3812  {
3813  PMM_IMAGE_SECTION_OBJECT ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
3814 
3815  _SEH2_TRY
3816  {
3817  PSECTION_IMAGE_INFORMATION Sii = (PSECTION_IMAGE_INFORMATION)SectionInformation;
3818  *Sii = ImageSectionObject->ImageInformation;
3819  if (ResultLength != NULL)
3820  {
3821  *ResultLength = sizeof(*Sii);
3822  }
3823  }
3825  {
3827  }
3828  _SEH2_END;
3829  }
3830  else
3831  {
3832  _SEH2_TRY
3833  {
3834  PSECTION_IMAGE_INFORMATION Sii = (PSECTION_IMAGE_INFORMATION)SectionInformation;
3835  *Sii = *Section->Segment->u2.ImageInformation;
3836  if (ResultLength != NULL)
3837  *ResultLength = sizeof(*Sii);
3838  }
3840  {
3842  }
3843  _SEH2_END;
3844  }
3845  break;
3846  }
3847  default:
3848  DPRINT1("Unknown SectionInformationClass: %d\n", SectionInformationClass);
3850  }
3851 
3852  ObDereferenceObject(Section);
3853 
3854  return Status;
3855 }
3856 
3857 /**********************************************************************
3858  * NAME EXPORTED
3859  * MmMapViewOfSection
3860  *
3861  * DESCRIPTION
3862  * Maps a view of a section into the virtual address space of a
3863  * process.
3864  *
3865  * ARGUMENTS
3866  * Section
3867  * Pointer to the section object.
3868  *
3869  * ProcessHandle
3870  * Pointer to the process.
3871  *
3872  * BaseAddress
3873  * Desired base address (or NULL) on entry;
3874  * Actual base address of the view on exit.
3875  *
3876  * ZeroBits
3877  * Number of high order address bits that must be zero.
3878  *
3879  * CommitSize
3880  * Size in bytes of the initially committed section of
3881  * the view.
3882  *
3883  * SectionOffset
3884  * Offset in bytes from the beginning of the section
3885  * to the beginning of the view.
3886  *
3887  * ViewSize
3888  * Desired length of map (or zero to map all) on entry
3889  * Actual length mapped on exit.
3890  *
3891  * InheritDisposition
3892  * Specified how the view is to be shared with
3893  * child processes.
3894  *
3895  * AllocationType
3896  * Type of allocation for the pages.
3897  *
3898  * Protect
3899  * Protection for the committed region of the view.
3900  *
3901  * RETURN VALUE
3902  * Status.
3903  *
3904  * @implemented
3905  */
3916  IN ULONG Protect)
3917 {
3918  PSECTION Section;
3921  BOOLEAN NotAtBase = FALSE;
3922 
3924  {
3925  DPRINT("Mapping ARM3 section into %s\n", Process->ImageFileName);
3927  Process,
3928  BaseAddress,
3929  ZeroBits,
3930  CommitSize,
3931  SectionOffset,
3932  ViewSize,
3935  Protect);
3936  }
3937 
3938  ASSERT(Process);
3939 
3941  {
3943  }
3944 
3945  /* FIXME: We should keep this, but it would break code checking equality */
3946  Protect &= ~PAGE_NOCACHE;
3947 
3948  Section = SectionObject;
3949  AddressSpace = &Process->Vm;
3950 
3951  if (Section->u.Flags.NoChange)
3953 
3955 
3956  if (Section->u.Flags.Image)
3957  {
3958  ULONG i;
3959  ULONG NrSegments;
3960  ULONG_PTR ImageBase;
3961  SIZE_T ImageSize;
3962  PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3963  PMM_SECTION_SEGMENT SectionSegments;
3964 
3965  ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
3966  SectionSegments = ImageSectionObject->Segments;
3967  NrSegments = ImageSectionObject->NrSegments;
3968 
3969  ASSERT(ImageSectionObject->RefCount > 0);
3970 
3971  ImageBase = (ULONG_PTR)*BaseAddress;
3972  if (ImageBase == 0)
3973  {
3974  ImageBase = (ULONG_PTR)ImageSectionObject->BasedAddress;
3975  }
3976 
3977  ImageSize = 0;
3978  for (i = 0; i < NrSegments; i++)
3979  {
3980  ULONG_PTR MaxExtent;
3981  MaxExtent = (ULONG_PTR)(SectionSegments[i].Image.VirtualAddress +
3982  SectionSegments[i].Length.QuadPart);
3983  ImageSize = max(ImageSize, MaxExtent);
3984  }
3985 
3986  ImageSectionObject->ImageInformation.ImageFileSize = (ULONG)ImageSize;
3987 
3988  /* Check for an illegal base address */
3989  if (((ImageBase + ImageSize) > (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS) ||
3990  ((ImageBase + ImageSize) < ImageSize))
3991  {
3992  ASSERT(*BaseAddress == NULL);
3993  ImageBase = ALIGN_DOWN_BY((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - ImageSize,
3995  NotAtBase = TRUE;
3996  }
3997  else if (ImageBase != ALIGN_DOWN_BY(ImageBase, MM_VIRTMEM_GRANULARITY))
3998  {
3999  ASSERT(*BaseAddress == NULL);
4000  ImageBase = ALIGN_DOWN_BY(ImageBase, MM_VIRTMEM_GRANULARITY);
4001  NotAtBase = TRUE;
4002  }
4003 
4004  /* Check there is enough space to map the section at that point. */
4006  PAGE_ROUND_UP(ImageSize)) != NULL)
4007  {
4008  /* Fail if the user requested a fixed base address. */
4009  if ((*BaseAddress) != NULL)
4010  {
4013  }
4014  /* Otherwise find a gap to map the image. */
4016  if (ImageBase == 0)
4017  {
4020  }
4021  /* Remember that we loaded image at a different base address */
4022  NotAtBase = TRUE;
4023  }
4024 
4025  for (i = 0; i < NrSegments; i++)
4026  {
4027  PVOID SBaseAddress = (PVOID)
4028  ((char*)ImageBase + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
4029  MmLockSectionSegment(&SectionSegments[i]);
4031  TRUE,
4032  &SectionSegments[i],
4033  &SBaseAddress,
4034  SectionSegments[i].Length.QuadPart,
4035  SectionSegments[i].Protection,
4036  0,
4037  0);
4038  MmUnlockSectionSegment(&SectionSegments[i]);
4039  if (!NT_SUCCESS(Status))
4040  {
4041  /* roll-back */
4042  while (i--)
4043  {
4044  SBaseAddress = ((char*)ImageBase + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
4045  MmLockSectionSegment(&SectionSegments[i]);
4046  MmUnmapViewOfSegment(AddressSpace, SBaseAddress);
4047  MmUnlockSectionSegment(&SectionSegments[i]);
4048  }
4049 
4051  return Status;
4052  }
4053  }
4054 
4055  *BaseAddress = (PVOID)ImageBase;
4056  *ViewSize = ImageSize;
4057 
4058  DPRINT("Mapped %p for section pointer %p\n", ImageSectionObject, ImageSectionObject->FileObject->SectionObjectPointer);
4059 
4060  /* One more map */
4061  InterlockedIncrement(&ImageSectionObject->MapCount);
4062  }
4063  else
4064  {
4066  LONGLONG ViewOffset;
4067 
4068  ASSERT(Segment->RefCount > 0);
4069 
4070  /* check for write access */
4073  {
4076  }
4077  /* check for read access */
4080  {
4083  }
4084  /* check for execute access */
4087  {
4090  }
4091 
4092  if (SectionOffset == NULL)
4093  {
4094  ViewOffset = 0;
4095  }
4096  else
4097  {
4098  ViewOffset = SectionOffset->QuadPart;
4099  }
4100 
4101  if ((ViewOffset % PAGE_SIZE) != 0)
4102  {
4104  return STATUS_MAPPED_ALIGNMENT;
4105  }
4106 
4107  if ((*ViewSize) == 0)
4108  {
4109  (*ViewSize) = Section->SizeOfSection.QuadPart - ViewOffset;
4110  }
4111  else if ((ExGetPreviousMode() == UserMode) &&
4112  (((*ViewSize)+ViewOffset) > Section->SizeOfSection.QuadPart) &&
4113  (!Section->u.Flags.Reserve))
4114  {
4115  /* Dubious */
4116  (*ViewSize) = MIN(Section->SizeOfSection.QuadPart - ViewOffset, SIZE_T_MAX - PAGE_SIZE);
4117  }
4118 
4120 
4123  FALSE,
4124  Segment,
4125  BaseAddress,
4126  *ViewSize,
4127  Protect,
4128  ViewOffset,
4131  if (!NT_SUCCESS(Status))
4132  {
4134  return Status;
4135  }
4136  }
4137 
4139 
4140  if (NotAtBase)
4142  else
4144 
4145  return Status;
4146 }
4147 
4148 /*
4149  * @unimplemented
4150  */
4151 BOOLEAN NTAPI
4154 {
4155  BOOLEAN Ret;
4157 
4158  /* Check whether an ImageSectionObject exists */
4159  if (SectionObjectPointer->ImageSectionObject != NULL)
4160  {
4161  DPRINT1("ERROR: File can't be truncated because it has an image section\n");
4162  return FALSE;
4163  }
4164 
4166  if (!Segment)
4167  {
4168  /* There is no data section. It's fine to do anything. */
4169  return TRUE;
4170  }
4171 
4173  if ((Segment->SectionCount == 0) ||
4174  ((Segment->SectionCount == 1) && (SectionObjectPointer->SharedCacheMap != NULL)))
4175  {
4176  /* If the cache is the only one holding a reference to the segment, then it's fine to resize */
4177  Ret = TRUE;
4178  }
4179  else
4180  {
4181  /* We can't shrink, but we can extend */
4182  Ret = NewFileSize->QuadPart >= Segment->RawLength.QuadPart;
4183 #if DBG
4184  if (!Ret)
4185  {
4186  DPRINT1("Cannot truncate data: New Size %I64d, Segment Size %I64d\n", NewFileSize->QuadPart, Segment->RawLength.QuadPart);
4187  }
4188 #endif
4189  }
4191  MmDereferenceSegment(Segment);
4192 
4193  DPRINT("FIXME: didn't check for outstanding write probes\n");
4194 
4195  return Ret;
4196 }
4197 
4198 static
4199 BOOLEAN
4201 {
4203 
4205 
4206  /* Loop over all entries */
4207  for (PageTable = RtlEnumerateGenericTable(&Segment->PageTable, TRUE);
4208  PageTable != NULL;
4210  {
4211  for (ULONG i = 0; i < _countof(PageTable->PageEntries); i++)
4212  {
4213  ULONG_PTR Entry = PageTable->PageEntries[i];
4215 
4216  if (!Entry)
4217  continue;
4218 
4220  {
4221  /* I/O ongoing or swap entry. Someone mapped this file as we were not looking */
4223  return FALSE;
4224  }
4225 
4226  /* Regular entry */
4229 
4230  /* Properly remove using the used API */
4231  Offset.QuadPart = PageTable->FileOffset.QuadPart + (i << PAGE_SHIFT);
4234  }
4235  }
4236 
4238 
4239  return TRUE;
4240 }
4241 
4242 /*
4243  * @implemented
4244  */
4245 BOOLEAN NTAPI
4248 {
4249  switch(FlushType)
4250  {
4251  case MmFlushForDelete:
4252  {
4253  /*
4254  * FIXME: Check for outstanding write probes on Data section.
4255  * How do we do that ?
4256  */
4257  }
4258  /* Fall-through */
4259  case MmFlushForWrite:
4260  {
4261  KIRQL OldIrql = MiAcquirePfnLock();
4262  PMM_IMAGE_SECTION_OBJECT ImageSectionObject = SectionObjectPointer->ImageSectionObject;
4263 
4264  DPRINT("Deleting or modifying %p\n", SectionObjectPointer);
4265 
4266  /* Wait for concurrent creation or deletion of image to be done */
4267  ImageSectionObject = SectionObjectPointer->ImageSectionObject;
4268  while (ImageSectionObject && (ImageSectionObject->SegFlags & (MM_SEGMENT_INCREATE | MM_SEGMENT_INDELETE)))
4269  {
4270  MiReleasePfnLock(OldIrql);
4272  OldIrql = MiAcquirePfnLock();
4273  ImageSectionObject = SectionObjectPointer->ImageSectionObject;
4274  }
4275 
4276  if (!ImageSectionObject)
4277  {
4278  DPRINT("No image section object. Accepting\n");
4279  /* Nothing to do */
4280  MiReleasePfnLock(OldIrql);
4281  return TRUE;
4282  }
4283 
4284  /* Do we have open sections or mappings on it ? */
4285  if ((ImageSectionObject->SectionCount) || (ImageSectionObject->MapCount))
4286  {
4287  /* We do. No way to delete it */
4288  MiReleasePfnLock(OldIrql);
4289  DPRINT("Denying. There are mappings open\n");
4290  return FALSE;
4291  }
4292 
4293  /* There are no sections open on it, but we must still have pages around. Discard everything */
4294  ImageSectionObject->SegFlags |= MM_IMAGE_SECTION_FLUSH_DELETE;
4295  InterlockedIncrement64(&ImageSectionObject->RefCount);
4296  MiReleasePfnLock(OldIrql);
4297 
4298  DPRINT("Purging\n");
4299 
4300  for (ULONG i = 0; i < ImageSectionObject->NrSegments; i++)
4301  {
4302  if (!MiPurgeImageSegment(&ImageSectionObject->Segments[i]))
4303  break;
4304  }
4305 
4306  /* Grab lock again */
4307  OldIrql = MiAcquirePfnLock();
4308 
4309  if (!(ImageSectionObject->SegFlags & MM_IMAGE_SECTION_FLUSH_DELETE))
4310  {
4311  /*
4312  * Someone actually created a section while we were not looking.
4313  * Drop our ref and deny.
4314  * MmDereferenceSegmentWithLock releases Pfn lock
4315  */
4316  MmDereferenceSegmentWithLock(&ImageSectionObject->Segments[0], OldIrql);
4317  return FALSE;
4318  }
4319 
4320  /* We should be the last one holding a ref here. */
4321  ASSERT(ImageSectionObject->RefCount == 1);
4322  ASSERT(ImageSectionObject->SectionCount == 0);
4323 
4324  /* Dereference the first segment, this will free everything & release the lock */
4325  MmDereferenceSegmentWithLock(&ImageSectionObject->Segments[0], OldIrql);
4326  return TRUE;
4327  }
4328  }
4329  return FALSE;
4330 }
4331 
4332 /*
4333  * @implemented
4334  */
4335 NTSTATUS
4336 NTAPI
4338  OUT PVOID * MappedBase,
4340 {
4342 
4343  SectionOffset.QuadPart = 0;
4344 
4346 }
4347 
4348 NTSTATUS
4349 NTAPI
4356  )
4357 {
4358  PSECTION Section = SectionObject;
4361  NTSTATUS Status;
4362 
4364 
4365  PAGED_CODE();
4366 
4368  {
4370  &MmSession,
4371  MappedBase,
4372  ViewSize,
4373  SectionOffset);
4374  }
4375 
4376  DPRINT("MmMapViewInSystemSpaceEx() called\n");
4377 
4378  /* unsupported for now */
4379  ASSERT(Section->u.Flags.Image == 0);
4380 
4381  Section = SectionObject;
4382  Segment = (PMM_SECTION_SEGMENT)Section->Segment;
4383 
4384  if (*ViewSize == 0)
4385  {
4386  LONGLONG MapSizeLL;
4387 
4388  /* Page-align the mapping */
4389  SectionOffset->LowPart = PAGE_ROUND_DOWN(SectionOffset->LowPart);
4390 
4391  if (!NT_SUCCESS(RtlLongLongSub(Section->SizeOfSection.QuadPart, SectionOffset->QuadPart, &MapSizeLL)))
4392  return STATUS_INVALID_VIEW_SIZE;
4393 
4394  if (!NT_SUCCESS(RtlLongLongToSIZET(MapSizeLL, ViewSize)))
4395  return STATUS_INVALID_VIEW_SIZE;
4396  }
4397  else
4398  {
4399  LONGLONG HelperLL;
4400 
4401  /* Get the map end */
4402  if (!NT_SUCCESS(RtlLongLongAdd(SectionOffset->QuadPart, *ViewSize, &HelperLL)))
4403  return STATUS_INVALID_VIEW_SIZE;
4404 
4405  /* Round it up, if needed */
4406  if (HelperLL % PAGE_SIZE)
4407  {
4408  if (!NT_SUCCESS(RtlLongLongAdd(HelperLL, PAGE_SIZE - (HelperLL % PAGE_SIZE), &HelperLL)))
4409  return STATUS_INVALID_VIEW_SIZE;
4410  }
4411 
4412  /* Now that we have the mapping end, we can align down its start */
4413  SectionOffset->LowPart = PAGE_ROUND_DOWN(SectionOffset->LowPart);
4414 
4415  /* Get the new size */
4416  if (!NT_SUCCESS(RtlLongLongSub(HelperLL, SectionOffset->QuadPart, &HelperLL)))
4417  return STATUS_INVALID_VIEW_SIZE;
4418 
4419  if (!NT_SUCCESS(RtlLongLongToSIZET(HelperLL, ViewSize)))
4420  return STATUS_INVALID_VIEW_SIZE;
4421  }
4422 
4424 
4426 
4428 
4430  Section->u.Flags.Image,
4431  Segment,
4432  MappedBase,
4433  *ViewSize,
4435  SectionOffset->QuadPart,
4436  SEC_RESERVE);
4437 
4440 
4441  return Status;
4442 }
4443 
4444 /* This function must be called with adress space lock held */
4445 NTSTATUS
4446 NTAPI
4448 {
4449  DPRINT("MmUnmapViewInSystemSpace() called\n");
4450 
4452 }
4453 
4454 /**********************************************************************
4455  * NAME EXPORTED
4456  * MmCreateSection@
4457  *
4458  * DESCRIPTION
4459  * Creates a section object.
4460  *
4461  * ARGUMENTS
4462  * SectionObject (OUT)
4463  * Caller supplied storage for the resulting pointer
4464  * to a SECTION_OBJECT instance;
4465  *
4466  * DesiredAccess
4467  * Specifies the desired access to the section can be a
4468  * combination of:
4469  * STANDARD_RIGHTS_REQUIRED |
4470  * SECTION_QUERY |
4471  * SECTION_MAP_WRITE |
4472  * SECTION_MAP_READ |
4473  * SECTION_MAP_EXECUTE
4474  *
4475  * ObjectAttributes [OPTIONAL]
4476  * Initialized attributes for the object can be used
4477  * to create a named section;
4478  *
4479  * MaximumSize
4480  * Maximizes the size of the memory section. Must be
4481  * non-NULL for a page-file backed section.
4482  * If value specified for a mapped file and the file is
4483  * not large enough, file will be extended.
4484  *
4485  * SectionPageProtection
4486  * Can be a combination of:
4487  * PAGE_READONLY |
4488  * PAGE_READWRITE |
4489  * PAGE_WRITEONLY |
4490  * PAGE_WRITECOPY
4491  *
4492  * AllocationAttributes
4493  * Can be a combination of:
4494  * SEC_IMAGE |
4495  * SEC_RESERVE
4496  *
4497  * FileHandle
4498  * Handle to a file to create a section mapped to a file
4499  * instead of a memory backed section;
4500  *
4501  * File
4502  * Unknown.
4503  *
4504  * RETURN VALUE
4505  * Status.
4506  *
4507  * @implemented
4508  */
4518 {
4519  NTSTATUS Status;
4520  ULONG Protection;
4521  PSECTION *SectionObject = (PSECTION *)Section;
4522  BOOLEAN FileLock = FALSE;
4523 
4524  /* Check if an ARM3 section is being created instead */
4526  {
4527  if (!(FileObject) && !(FileHandle))
4528  {
4529  return MmCreateArm3Section(Section,
4530  DesiredAccess,
4532  MaximumSize,
4534  AllocationAttributes &~ 1,
4535  FileHandle,
4536  FileObject);
4537  }
4538  }
4539 
4540  /* Convert section flag to page flag */
4542 
4543  /* Check to make sure the protection is correct. Nt* does this already */
4545  if (Protection == MM_INVALID_PROTECTION)
4546  {
4547  DPRINT1("Page protection is invalid\n");
4549  }
4550 
4551  /* Check if this is going to be a data or image backed file section */
4552  if ((FileHandle) || (FileObject))
4553  {
4554  /* These cannot be mapped with large pages */
4556  {
4557  DPRINT1("Large pages cannot be used with an image mapping\n");
4559  }
4560 
4561  /* Did the caller pass a file object ? */
4562  if (FileObject)
4563  {
4564  /* Reference the object directly */
4566 
4567  /* We don't create image mappings with file objects */
4569  }
4570  else
4571  {
4572  /* Reference the file handle to get the object */
4574  MmMakeFileAccess[Protection],
4577  (PVOID*)&FileObject,
4578  NULL);
4579  if (!NT_SUCCESS(Status))
4580  {
4581  DPRINT1("Failed to get a handle to the FO: %lx\n", Status);
4582  return Status;
4583  }
4584 
4585  /* Lock the file */
4587  if (!NT_SUCCESS(Status))
4588  {
4590  return Status;
4591  }
4592 
4593  FileLock = TRUE;
4594 
4595  /* Deny access if there are writes on the file */
4596 #if 0
4598  {
4599  DPRINT1("Cannot create image maps with writers open on the file!\n");
4601  goto Quit;
4602  }
4603 #else
4605  DPRINT1("Creating image map with writers open on the file!\n");
4606 #endif
4607  }
4608  }
4609  else
4610  {
4611  /* A handle must be supplied with SEC_IMAGE, as this is the no-handle path */
4613  }
4614 
4616  {
4618  DesiredAccess,
4620  MaximumSize,
4623  FileObject);
4624  }
4625 #ifndef NEWCC
4626  else if (FileObject != NULL)
4627  {
4629  DesiredAccess,
4631  MaximumSize,
4634  FileObject,
4635  FileHandle != NULL);
4636  }
4637 #else
4638  else if (FileHandle != NULL || FileObject != NULL)
4639  {
4641  DesiredAccess,
4643  MaximumSize,
4646  FileObject);
4647  }
4648 #endif
4649  else
4650  {
4651  /* All cases should be handled above */
4653  }
4654 
4655  if (FileLock)
4657  if (FileObject)
4659 
4660  return Status;
4661 }
4662 
4663 BOOLEAN
4664 NTAPI
4667  _In_ PVOID Address,
4668  _In_ ULONG Length)
4669 {
4671  BOOLEAN Ret = TRUE;
4673  LARGE_INTEGER SegmentOffset, RangeEnd;
4675 
4677 
4679  if (MemoryArea == NULL)
4680  {
4682  return FALSE;
4683  }
4684 
4685  /* Only supported in old Mm for now */
4687  /* For file mappings */
4689 
4690  Segment = MemoryArea->SectionData.Segment;
4692 
4694  + MemoryArea->SectionData.ViewOffset;
4696  + MemoryArea->SectionData.ViewOffset;
4697 
4698  while (SegmentOffset.QuadPart < RangeEnd.QuadPart)
4699  {
4701  if ((Entry == 0) || IS_SWAP_FROM_SSE(Entry))
4702  {
4703  Ret = FALSE;
4704  break;
4705  }
4706  SegmentOffset.QuadPart += PAGE_SIZE;
4707  }
4708 
4710 
4712  return Ret;
4713 }
4714 
4715 /* Like CcPurgeCache but for the in-memory segment */
4716 BOOLEAN
4717 NTAPI
4721  _In_ ULONG Length)
4722 {
4723  LARGE_INTEGER PurgeStart, PurgeEnd;
4725 
4727  if (!Segment)
4728  {
4729  /* Nothing to purge */
4730  return STATUS_SUCCESS;
4731  }
4732 
4733  PurgeStart.QuadPart = Offset ? Offset->QuadPart : 0LL;
4734  if (Length && Offset)
4735  {
4736  if (!NT_SUCCESS(RtlLongLongAdd(PurgeStart.QuadPart, Length, &PurgeEnd.QuadPart)))
4737  return FALSE;
4738  }
4739 
4741 
4742  if (!Length || !Offset)
4743  {
4744  /* We must calculate the length for ourselves */
4745  /* FIXME: All of this is suboptimal */
4746  ULONG ElemCount = RtlNumberGenericTableElements(&Segment->PageTable);
4747  /* No page. Nothing to purge */
4748  if (!ElemCount)
4749  {
4751  MmDereferenceSegment(Segment);
4752  return TRUE;
4753  }
4754 
4755