ReactOS  0.4.15-dev-3324-gda4e15f
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 
996 VOID
997 NTAPI
998 MmDereferenceSegmentWithLock(PMM_SECTION_SEGMENT Segment, KIRQL OldIrql)
999 {
1000  /* Lock the PFN lock because we mess around with SectionObjectPointers */
1001  if (OldIrql == MM_NOIRQL)
1002  {
1003  OldIrql = MiAcquirePfnLock();
1004  }
1005 
1006  if (InterlockedDecrement64(Segment->ReferenceCount) > 0)
1007  {
1008  /* Nothing to do yet */
1009  MiReleasePfnLock(OldIrql);
1010  return;
1011  }
1012 
1013  *Segment->Flags |= MM_SEGMENT_INDELETE;
1014 
1015  /* Flush the segment */
1016  if (*Segment->Flags & MM_DATAFILE_SEGMENT)
1017  {
1018  MiReleasePfnLock(OldIrql);
1019  /* Free the page table. This will flush any remaining dirty data */
1021 
1022  OldIrql = MiAcquirePfnLock();
1023  /* Delete the pointer on the file */
1024  ASSERT(Segment->FileObject->SectionObjectPointer->DataSectionObject == Segment);
1025  Segment->FileObject->SectionObjectPointer->DataSectionObject = NULL;
1026  MiReleasePfnLock(OldIrql);
1027  ObDereferenceObject(Segment->FileObject);
1028 
1030  }
1031  else
1032  {
1033  /* Most grotesque thing ever */
1034  PMM_IMAGE_SECTION_OBJECT ImageSectionObject = CONTAINING_RECORD(Segment->ReferenceCount, MM_IMAGE_SECTION_OBJECT, RefCount);
1035  PMM_SECTION_SEGMENT SectionSegments;
1036  ULONG NrSegments;
1037  ULONG i;
1038 
1039  /* Delete the pointer on the file */
1040  ASSERT(ImageSectionObject->FileObject->SectionObjectPointer->ImageSectionObject == ImageSectionObject);
1041  ImageSectionObject->FileObject->SectionObjectPointer->ImageSectionObject = NULL;
1042  MiReleasePfnLock(OldIrql);
1043 
1044  ObDereferenceObject(ImageSectionObject->FileObject);
1045 
1046  NrSegments = ImageSectionObject->NrSegments;
1047  SectionSegments = ImageSectionObject->Segments;
1048  for (i = 0; i < NrSegments; i++)
1049  {
1050  if (SectionSegments[i].Image.Characteristics & IMAGE_SCN_MEM_SHARED)
1051  {
1052  MmpFreePageFileSegment(&SectionSegments[i]);
1053  }
1054 
1055  MmFreePageTablesSectionSegment(&SectionSegments[i], NULL);
1056  }
1057 
1058  ExFreePoolWithTag(ImageSectionObject->Segments, TAG_MM_SECTION_SEGMENT);
1059  ExFreePoolWithTag(ImageSectionObject, TAG_MM_SECTION_SEGMENT);
1060  }
1061 }
1062 
1063 VOID
1064 NTAPI
1067 {
1068  ULONG_PTR Entry;
1069 
1071  if (Entry == 0)
1072  {
1073  DPRINT1("Entry == 0 for MmSharePageEntrySectionSegment\n");
1074  KeBugCheck(MEMORY_MANAGEMENT);
1075  }
1077  {
1078  DPRINT1("Maximum share count reached\n");
1079  KeBugCheck(MEMORY_MANAGEMENT);
1080  }
1081  if (IS_SWAP_FROM_SSE(Entry))
1082  {
1083  KeBugCheck(MEMORY_MANAGEMENT);
1084  }
1086 }
1087 
1088 BOOLEAN
1089 NTAPI
1093  BOOLEAN Dirty,
1094  BOOLEAN PageOut,
1095  ULONG_PTR *InEntry)
1096 {
1097  ULONG_PTR Entry = InEntry ? *InEntry : MmGetPageEntrySectionSegment(Segment, Offset);
1099  BOOLEAN IsDataMap = BooleanFlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT);
1100  SWAPENTRY SwapEntry;
1101 
1102  if (Entry == 0)
1103  {
1104  DPRINT1("Entry == 0 for MmUnsharePageEntrySectionSegment\n");
1105  KeBugCheck(MEMORY_MANAGEMENT);
1106  }
1107  if (SHARE_COUNT_FROM_SSE(Entry) == 0)
1108  {
1109  DPRINT1("Zero share count for unshare (Seg %p Offset %x Page %x)\n", Segment, Offset->LowPart, PFN_FROM_SSE(Entry));
1110  KeBugCheck(MEMORY_MANAGEMENT);
1111  }
1112  if (IS_SWAP_FROM_SSE(Entry))
1113  {
1114  KeBugCheck(MEMORY_MANAGEMENT);
1115  }
1116  Entry = DECREF_SSE(Entry);
1117  if (Dirty) Entry = DIRTY_SSE(Entry);
1118 
1119  /* If we are paging-out, pruning the page for real will be taken care of in MmCheckDirtySegment */
1120  if ((SHARE_COUNT_FROM_SSE(Entry) > 0) || PageOut)
1121  {
1122  /* Update the page mapping in the segment and we're done */
1124  return FALSE;
1125  }
1126 
1127  /* We are pruning the last mapping on this page. See if we can keep it a bit more. */
1128  ASSERT(!PageOut);
1129 
1130  if (IsDataMap)
1131  {
1132  /* We can always keep memory in for data maps */
1134  return FALSE;
1135  }
1136 
1137  if (!FlagOn(Segment->Image.Characteristics, IMAGE_SCN_MEM_SHARED))
1138  {
1139  ASSERT(Segment->WriteCopy);
1142  /* So this must have been a read-only page. Keep it ! */
1144  return FALSE;
1145  }
1146 
1147  /*
1148  * So this is a page for a shared section of a DLL.
1149  * We can keep it if it is not dirty.
1150  */
1151  SwapEntry = MmGetSavedSwapEntryPage(Page);
1152  if ((SwapEntry == 0) && !IS_DIRTY_SSE(Entry))
1153  {
1155  return FALSE;
1156  }
1157 
1158  /* No more processes are referencing this shared dirty page. Ditch it. */
1159  if (SwapEntry)
1160  {
1162  MmFreeSwapPage(SwapEntry);
1163  }
1166  return TRUE;
1167 }
1168 
1169 static
1170 NTSTATUS
1171 MiCopyFromUserPage(PFN_NUMBER DestPage, const VOID *SrcAddress)
1172 {
1174  KIRQL Irql;
1175  PVOID DestAddress;
1176 
1178  DestAddress = MiMapPageInHyperSpace(Process, DestPage, &Irql);
1179  if (DestAddress == NULL)
1180  {
1181  return STATUS_NO_MEMORY;
1182  }
1183  ASSERT((ULONG_PTR)DestAddress % PAGE_SIZE == 0);
1184  ASSERT((ULONG_PTR)SrcAddress % PAGE_SIZE == 0);
1185  RtlCopyMemory(DestAddress, SrcAddress, PAGE_SIZE);
1186  MiUnmapPageInHyperSpace(Process, DestAddress, Irql);
1187  return STATUS_SUCCESS;
1188 }
1189 
1190 static
1191 NTSTATUS
1192 NTAPI
1196  _In_ ULONG Length,
1197  _In_opt_ PLARGE_INTEGER ValidDataLength,
1198  _In_ BOOLEAN SetDirty)
1199 {
1200  /* Let's use a 64K granularity. */
1201  LONGLONG RangeStart, RangeEnd;
1202  NTSTATUS Status;
1203  PFILE_OBJECT FileObject = Segment->FileObject;
1204 
1205  /* Calculate our range, aligned on 64K if possible. */
1206  Status = RtlLongLongAdd(Offset, Length, &RangeEnd);
1208  if (!NT_SUCCESS(Status))
1209  return Status;
1210 
1211  /* If the file is not random access and we are not the page out thread
1212  * read a 64K Chunk. */
1214  && !FlagOn(FileObject->Flags, FO_RANDOM_ACCESS))
1215  {
1216  RangeStart = Offset - (Offset % _64K);
1217  if (RangeEnd % _64K)
1218  RangeEnd += _64K - (RangeEnd % _64K);
1219  }
1220  else
1221  {
1222  RangeStart = Offset - (Offset % PAGE_SIZE);
1223  if (RangeEnd % PAGE_SIZE)
1224  RangeEnd += PAGE_SIZE - (RangeEnd % PAGE_SIZE);
1225  }
1226 
1227  /* Clamp if needed */
1228  if (!FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT))
1229  {
1230  if (RangeEnd > Segment->RawLength.QuadPart)
1231  RangeEnd = Segment->RawLength.QuadPart;
1232  }
1233 
1234  /* Let's gooooooooo */
1235  for ( ; RangeStart < RangeEnd; RangeStart += _64K)
1236  {
1237  /* First take a look at where we miss pages */
1238  ULONG ToReadPageBits = 0;
1239  LONGLONG ChunkEnd = RangeStart + _64K;
1240 
1241  if (ChunkEnd > RangeEnd)
1242  ChunkEnd = RangeEnd;
1243 
1245  for (LONGLONG ChunkOffset = RangeStart; ChunkOffset < ChunkEnd; ChunkOffset += PAGE_SIZE)
1246  {
1247  LARGE_INTEGER CurrentOffset;
1248 
1249  CurrentOffset.QuadPart = ChunkOffset;
1251 
1252  /* Let any pending read proceed */
1253  while (MM_IS_WAIT_PTE(Entry))
1254  {
1256 
1258 
1260  Entry = MmGetPageEntrySectionSegment(Segment, &CurrentOffset);
1261  }
1262 
1263  if (Entry != 0)
1264  {
1265  /* Dirtify it if it's a resident page and we're asked to */
1266  if (SetDirty && !IS_SWAP_FROM_SSE(Entry))
1268  continue;
1269  }
1270 
1271  ToReadPageBits |= 1UL << ((ChunkOffset - RangeStart) >> PAGE_SHIFT);
1272 
1273  /* Put a wait entry here */
1274  MmSetPageEntrySectionSegment(Segment, &CurrentOffset, MAKE_SWAP_SSE(MM_WAIT_ENTRY));
1275  }
1277 
1278  if (ToReadPageBits == 0)
1279  {
1280  /* Nothing to do for this chunk */
1281  continue;
1282  }
1283 
1284  /* Now perform the actual read */
1285  LONGLONG ChunkOffset = RangeStart;
1286  while (ChunkOffset < ChunkEnd)
1287  {
1288  /* Move forward if there is a hole */
1289  ULONG BitSet;
1290  if (!_BitScanForward(&BitSet, ToReadPageBits))
1291  {
1292  /* Nothing more to read */
1293  break;
1294  }
1295  ToReadPageBits >>= BitSet;
1296  ChunkOffset += BitSet * PAGE_SIZE;
1297  ASSERT(ChunkOffset < ChunkEnd);
1298 
1299  /* Get the range we have to read */
1300  _BitScanForward(&BitSet, ~ToReadPageBits);
1301  ULONG ReadLength = BitSet * PAGE_SIZE;
1302 
1303  ASSERT(ReadLength <= _64K);
1304 
1305  /* Clamp (This is for image mappings */
1306  if ((ChunkOffset + ReadLength) > ChunkEnd)
1307  ReadLength = ChunkEnd - ChunkOffset;
1308 
1309  ASSERT(ReadLength != 0);
1310 
1311  /* Allocate a MDL */
1313  if (!Mdl)
1314  {
1315  /* Damn. Roll-back. */
1317  while (ChunkOffset < ChunkEnd)
1318  {
1319  if (ToReadPageBits & 1)
1320  {
1321  LARGE_INTEGER CurrentOffset;
1322  CurrentOffset.QuadPart = ChunkOffset;
1324  MmSetPageEntrySectionSegment(Segment, &CurrentOffset, 0);
1325  }
1326  ToReadPageBits >>= 1;
1327  ChunkOffset += PAGE_SIZE;
1328  }
1331  }
1332 
1333  /* Get our pages */
1334  PPFN_NUMBER Pages = MmGetMdlPfnArray(Mdl);
1336  for (UINT i = 0; i < BYTES_TO_PAGES(ReadLength); i++)
1337  {
1338  /* MmRequestPageMemoryConsumer succeeds or bugchecks */
1340  }
1341  Mdl->MdlFlags |= MDL_PAGES_LOCKED | MDL_IO_PAGE_READ;
1342 
1344  FileOffset.QuadPart = Segment->Image.FileOffset + ChunkOffset;
1345 
1346  /* Clamp to VDL */
1347  if (ValidDataLength && ((FileOffset.QuadPart + ReadLength) > ValidDataLength->QuadPart))
1348  {
1349  if (FileOffset.QuadPart > ValidDataLength->QuadPart)
1350  {
1351  /* Great, nothing to read. */
1352  goto AssignPagesToSegment;
1353  }
1354 
1355  Mdl->Size = (FileOffset.QuadPart + ReadLength) - ValidDataLength->QuadPart;
1356  }
1357 
1358  KEVENT Event;
1360 
1361  /* Disable APCs */
1362  KIRQL OldIrql;
1364 
1367  if (Status == STATUS_PENDING)
1368  {
1370  Status = Iosb.Status;
1371  }
1372 
1373  if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
1374  {
1375  MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
1376  }
1377 
1379 
1380  if (Status == STATUS_END_OF_FILE)
1381  {
1382  DPRINT1("Got STATUS_END_OF_FILE at offset %I64d for file %wZ.\n", FileOffset.QuadPart, &FileObject->FileName);
1384  }
1385 
1386  if (!NT_SUCCESS(Status))
1387  {
1388  /* Damn. Roll back. */
1389  for (UINT i = 0; i < BYTES_TO_PAGES(ReadLength); i++)
1391 
1393  while (ChunkOffset < ChunkEnd)
1394  {
1395  if (ToReadPageBits & 1)
1396  {
1397  LARGE_INTEGER CurrentOffset;
1398  CurrentOffset.QuadPart = ChunkOffset;
1400  MmSetPageEntrySectionSegment(Segment, &CurrentOffset, 0);
1401  }
1402  ToReadPageBits >>= 1;
1403  ChunkOffset += PAGE_SIZE;
1404  }
1406  IoFreeMdl(Mdl);;
1407  return Status;
1408  }
1409 
1410 AssignPagesToSegment:
1412 
1413  for (UINT i = 0; i < BYTES_TO_PAGES(ReadLength); i++)
1414  {
1415  ULONG_PTR Entry = MAKE_SSE(Pages[i] << PAGE_SHIFT, 0);
1416  LARGE_INTEGER CurrentOffset;
1417  CurrentOffset.QuadPart = ChunkOffset + (i * PAGE_SIZE);
1418 
1420 
1421  if (SetDirty)
1422  Entry = DIRTY_SSE(Entry);
1423 
1424  MmSetPageEntrySectionSegment(Segment, &CurrentOffset, Entry);
1425  }
1426 
1428 
1429  IoFreeMdl(Mdl);
1430  ToReadPageBits >>= BitSet;
1431  ChunkOffset += BitSet * PAGE_SIZE;
1432  }
1433  }
1434 
1435  return STATUS_SUCCESS;
1436 }
1437 
1438 static VOID
1442  ULONG OldType,
1443  ULONG OldProtect,
1444  ULONG NewType,
1445  ULONG NewProtect)
1446 {
1449  BOOLEAN DoCOW = FALSE;
1450  ULONG i;
1452 
1454  ASSERT(MemoryArea != NULL);
1455  Segment = MemoryArea->SectionData.Segment;
1457 
1458  if ((Segment->WriteCopy) &&
1460  {
1461  DoCOW = TRUE;
1462  }
1463 
1464  if (OldProtect != NewProtect)
1465  {
1466  for (i = 0; i < PAGE_ROUND_UP(RegionSize) / PAGE_SIZE; i++)
1467  {
1468  SWAPENTRY SwapEntry;
1469  PVOID Address = (char*)BaseAddress + (i * PAGE_SIZE);
1471 
1472  /* Wait for a wait entry to disappear */
1473  do
1474  {
1475  MmGetPageFileMapping(Process, Address, &SwapEntry);
1476  if (SwapEntry != MM_WAIT_ENTRY)
1477  break;
1480  YieldProcessor();
1483  }
1484  while (TRUE);
1485 
1486  /*
1487  * If we doing COW for this segment then check if the page is
1488  * already private.
1489  */
1491  {
1493  ULONG_PTR Entry;
1494  PFN_NUMBER Page;
1495 
1497  + MemoryArea->SectionData.ViewOffset;
1499  /*
1500  * An MM_WAIT_ENTRY is ok in this case... It'll just count as
1501  * IS_SWAP_FROM_SSE and we'll do the right thing.
1502  */
1504 
1507  {
1508  Protect = NewProtect;
1509  }
1510  }
1511 
1513  {
1515  Protect);
1516  }
1517  }
1518  }
1519 
1521 }
1522 
1523 NTSTATUS
1524 NTAPI
1527  PVOID Address,
1528  BOOLEAN Locked)
1529 {
1531  PFN_NUMBER Page;
1532  NTSTATUS Status;
1534  ULONG_PTR Entry;
1535  ULONG_PTR Entry1;
1536  ULONG Attributes;
1538  BOOLEAN HasSwapEntry;
1539  PVOID PAddress;
1541  SWAPENTRY SwapEntry;
1542 
1543  ASSERT(Locked);
1544 
1545  /*
1546  * There is a window between taking the page fault and locking the
1547  * address space when another thread could load the page so we check
1548  * that.
1549  */
1551  {
1552  return STATUS_SUCCESS;
1553  }
1554 
1556  {
1557  return STATUS_ACCESS_VIOLATION;
1558  }
1559 
1560  /*
1561  * Check for the virtual memory area being deleted.
1562  */
1564  {
1565  return STATUS_UNSUCCESSFUL;
1566  }
1567 
1568  PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1569  Offset.QuadPart = (ULONG_PTR)PAddress - MA_GetStartingAddress(MemoryArea)
1570  + MemoryArea->SectionData.ViewOffset;
1571 
1572  Segment = MemoryArea->SectionData.Segment;
1574  &MemoryArea->SectionData.RegionListHead,
1575  Address, NULL);
1576  ASSERT(Region != NULL);
1577 
1578  /* Check for a NOACCESS mapping */
1579  if (Region->Protect & PAGE_NOACCESS)
1580  {
1581  return STATUS_ACCESS_VIOLATION;
1582  }
1583 
1584  if (Region->Protect & PAGE_GUARD)
1585  {
1586  /* Remove it */
1588  &MemoryArea->SectionData.RegionListHead,
1589  Address, PAGE_SIZE, Region->Type, Region->Protect & ~PAGE_GUARD,
1591 
1592  if (!NT_SUCCESS(Status))
1593  {
1594  DPRINT1("Removing PAGE_GUARD protection failed : 0x%08x.\n", Status);
1595  }
1596 
1598  }
1599 
1600  HasSwapEntry = MmIsPageSwapEntry(Process, Address);
1601 
1602  /* See if we should use a private page */
1603  if (HasSwapEntry)
1604  {
1605  SWAPENTRY DummyEntry;
1606 
1607  MmGetPageFileMapping(Process, Address, &SwapEntry);
1608  if (SwapEntry == MM_WAIT_ENTRY)
1609  {
1611  YieldProcessor();
1614  }
1615 
1616  /*
1617  * Must be private page we have swapped out.
1618  */
1619 
1620  /*
1621  * Sanity check
1622  */
1623  MmDeletePageFileMapping(Process, Address, &DummyEntry);
1624  ASSERT(DummyEntry == SwapEntry);
1625 
1626  /* Tell everyone else we are serving the fault. */
1627  MmCreatePageFileMapping(Process, Address, MM_WAIT_ENTRY);
1628 
1631  if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1632  if (!Process) MI_SET_PROCESS2("Kernel Section");
1634  if (!NT_SUCCESS(Status))
1635  {
1636  KeBugCheck(MEMORY_MANAGEMENT);
1637  }
1638 
1639  Status = MmReadFromSwapPage(SwapEntry, Page);
1640  if (!NT_SUCCESS(Status))
1641  {
1642  DPRINT1("MmReadFromSwapPage failed, status = %x\n", Status);
1643  KeBugCheck(MEMORY_MANAGEMENT);
1644  }
1645 
1647  MmDeletePageFileMapping(Process, PAddress, &DummyEntry);
1648  ASSERT(DummyEntry == MM_WAIT_ENTRY);
1649 
1651  PAddress,
1652  Region->Protect,
1653  Page);
1654  if (!NT_SUCCESS(Status))
1655  {
1656  DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
1657  KeBugCheck(MEMORY_MANAGEMENT);
1658  return Status;
1659  }
1660 
1661  /*
1662  * Store the swap entry for later use.
1663  */
1664  MmSetSavedSwapEntryPage(Page, SwapEntry);
1665 
1666  /*
1667  * Add the page to the process's working set
1668  */
1670  /*
1671  * Finish the operation
1672  */
1673  DPRINT("Address 0x%p\n", Address);
1674  return STATUS_SUCCESS;
1675  }
1676 
1677  /*
1678  * Lock the segment
1679  */
1681 
1682  /*
1683  * Satisfying a page fault on a map of /Device/PhysicalMemory is easy
1684  */
1685  if ((*Segment->Flags) & MM_PHYSICALMEMORY_SEGMENT)
1686  {
1688  /*
1689  * Just map the desired physical page
1690  */
1691  Page = (PFN_NUMBER)(Offset.QuadPart >> PAGE_SHIFT);
1693  PAddress,
1694  Region->Protect,
1695  Page);
1696  if (!NT_SUCCESS(Status))
1697  {
1698  DPRINT("MmCreateVirtualMappingUnsafe failed, not out of memory\n");
1699  KeBugCheck(MEMORY_MANAGEMENT);
1700  return Status;
1701  }
1702 
1703  /*
1704  * Cleanup and release locks
1705  */
1706  DPRINT("Address 0x%p\n", Address);
1707  return STATUS_SUCCESS;
1708  }
1709 
1710  /*
1711  * Check if this page needs to be mapped COW
1712  */
1713  if ((Segment->WriteCopy) &&
1714  (Region->Protect == PAGE_READWRITE || Region->Protect == PAGE_EXECUTE_READWRITE))
1715  {
1717  }
1718  else
1719  {
1720  Attributes = Region->Protect;
1721  }
1722 
1723 
1724  /*
1725  * Get the entry corresponding to the offset within the section
1726  */
1728  if (Entry == 0)
1729  {
1730  /*
1731  * If the entry is zero, then we need to load the page.
1732  */
1733  if ((Offset.QuadPart >= (LONGLONG)PAGE_ROUND_UP(Segment->RawLength.QuadPart)) && (MemoryArea->VadNode.u.VadFlags.VadType == VadImageMap))
1734  {
1735  /* We are beyond the data which is on file. Just get a new page. */
1737  if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1738  if (!Process) MI_SET_PROCESS2("Kernel Section");
1742 
1744  if (!NT_SUCCESS(Status))
1745  {
1746  DPRINT1("Unable to create virtual mapping\n");
1747  KeBugCheck(MEMORY_MANAGEMENT);
1748  }
1749  ASSERT(MmIsPagePresent(Process, PAddress));
1750  if (Process)
1752 
1753  DPRINT("Address 0x%p\n", Address);
1754  return STATUS_SUCCESS;
1755  }
1756 
1759 
1760  /* The data must be paged in. Lock the file, so that the VDL doesn't get updated behind us. */
1761  FsRtlAcquireFileExclusive(Segment->FileObject);
1762 
1763  PFSRTL_COMMON_FCB_HEADER FcbHeader = Segment->FileObject->FsContext;
1764 
1766 
1767  FsRtlReleaseFile(Segment->FileObject);
1768 
1769  /* Lock address space again */
1771  if (!NT_SUCCESS(Status))
1772  {
1773  /* Damn */
1774  DPRINT1("Failed to page data in!\n");
1775  return STATUS_IN_PAGE_ERROR;
1776  }
1777 
1778  /* Everything went fine. Restart the operation */
1780  }
1781  else if (IS_SWAP_FROM_SSE(Entry))
1782  {
1783  SWAPENTRY SwapEntry;
1784 
1785  SwapEntry = SWAPENTRY_FROM_SSE(Entry);
1786 
1787  /* See if a page op is running on this segment. */
1788  if (SwapEntry == MM_WAIT_ENTRY)
1789  {
1792  YieldProcessor();
1795  }
1796 
1797  /*
1798  * Release all our locks and read in the page from disk
1799  */
1802 
1805  if (!NT_SUCCESS(Status))
1806  {
1807  KeBugCheck(MEMORY_MANAGEMENT);
1808  }
1809 
1810  Status = MmReadFromSwapPage(SwapEntry, Page);
1811  if (!NT_SUCCESS(Status))
1812  {
1813  KeBugCheck(MEMORY_MANAGEMENT);
1814  }
1815 
1816  /*
1817  * Relock the address space and segment
1818  */
1821 
1822  /*
1823  * Check the entry. No one should change the status of a page
1824  * that has a pending page-in.
1825  */
1827  if (Entry1 != MAKE_SWAP_SSE(MM_WAIT_ENTRY))
1828  {
1829  DPRINT1("Someone changed ppte entry while we slept (%x vs %x)\n", Entry, Entry1);
1830  KeBugCheck(MEMORY_MANAGEMENT);
1831  }
1832 
1833  /*
1834  * Save the swap entry.
1835  */
1836  MmSetSavedSwapEntryPage(Page, SwapEntry);
1837 
1838  /* Map the page into the process address space */
1840  PAddress,
1841  Attributes,
1842  Page);
1843  if (!NT_SUCCESS(Status))
1844  {
1845  DPRINT1("Unable to create virtual mapping\n");
1846  KeBugCheck(MEMORY_MANAGEMENT);
1847  }
1848  if (Process)
1850 
1851  /*
1852  * Mark the offset within the section as having valid, in-memory
1853  * data
1854  */
1855  Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
1858 
1859  DPRINT("Address 0x%p\n", Address);
1860  return STATUS_SUCCESS;
1861  }
1862  else
1863  {
1864  /* We already have a page on this section offset. Map it into the process address space. */
1865  Page = PFN_FROM_SSE(Entry);
1866 
1868  PAddress,
1869  Attributes,
1870  Page);
1871  if (!NT_SUCCESS(Status))
1872  {
1873  DPRINT1("Unable to create virtual mapping\n");
1874  KeBugCheck(MEMORY_MANAGEMENT);
1875  }
1876 
1877  if (Process)
1879 
1880  /* Take a reference on it */
1883 
1884  DPRINT("Address 0x%p\n", Address);
1885  return STATUS_SUCCESS;
1886  }
1887 }
1888 
1889 NTSTATUS
1890 NTAPI
1893  PVOID Address,
1894  BOOLEAN Locked)
1895 {
1897  PFN_NUMBER OldPage;
1898  PFN_NUMBER NewPage;
1899  PVOID PAddress;
1902  ULONG_PTR Entry;
1904  BOOLEAN Cow = FALSE;
1905  ULONG NewProtect;
1906 
1907  DPRINT("MmAccessFaultSectionView(%p, %p, %p)\n", AddressSpace, MemoryArea, Address);
1908 
1909  /* Get the region for this address */
1911  &MemoryArea->SectionData.RegionListHead,
1912  Address, NULL);
1913  ASSERT(Region != NULL);
1914  if (!(Region->Protect & PAGE_IS_WRITABLE))
1915  return STATUS_ACCESS_VIOLATION;
1916 
1917  /* Make sure we have a page mapping for this address. */
1919  {
1921  if (!NT_SUCCESS(Status))
1922  {
1923  /* This is invalid access ! */
1924  return Status;
1925  }
1926  }
1927 
1928  /*
1929  * Check if the page has already been set readwrite
1930  */
1932  {
1933  DPRINT("Address 0x%p\n", Address);
1934  return STATUS_SUCCESS;
1935  }
1936 
1937  /* Check if we are doing Copy-On-Write */
1938  Segment = MemoryArea->SectionData.Segment;
1939  Cow = Segment->WriteCopy || (Region->Protect & PAGE_IS_WRITECOPY);
1940 
1941  if (!Cow)
1942  {
1943  /* Simply update page protection and we're done */
1944  MmSetPageProtect(Process, Address, Region->Protect);
1945  return STATUS_SUCCESS;
1946  }
1947 
1948  /* Calculate the new protection & check if we should update the region */
1949  NewProtect = Region->Protect;
1951  {
1953  if (Region->Protect & PAGE_IS_EXECUTABLE)
1955  else
1958  &MemoryArea->SectionData.RegionListHead,
1961  }
1962 
1963  /*
1964  * Find the offset of the page
1965  */
1966  PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1967  Offset.QuadPart = (ULONG_PTR)PAddress - MA_GetStartingAddress(MemoryArea)
1968  + MemoryArea->SectionData.ViewOffset;
1969 
1970  /* Get the page mapping this section offset. */
1973 
1974  /* Get the current page mapping for the process */
1975  ASSERT(MmIsPagePresent(Process, PAddress));
1976  OldPage = MmGetPfnForProcess(Process, PAddress);
1977  ASSERT(OldPage != 0);
1978 
1979  if (IS_SWAP_FROM_SSE(Entry) ||
1980  PFN_FROM_SSE(Entry) != OldPage)
1981  {
1983  /* This is a private page. We must only change the page protection. */
1984  MmSetPageProtect(Process, PAddress, NewProtect);
1985  return STATUS_SUCCESS;
1986  }
1987 
1988  /*
1989  * Allocate a page
1990  */
1992  {
1993  KeBugCheck(MEMORY_MANAGEMENT);
1994  }
1995 
1996  /*
1997  * Copy the old page
1998  */
1999  NT_VERIFY(NT_SUCCESS(MiCopyFromUserPage(NewPage, PAddress)));
2000 
2001  /*
2002  * Unshare the old page.
2003  */
2004  DPRINT("Swapping page (Old %x New %x)\n", OldPage, NewPage);
2005  MmDeleteVirtualMapping(Process, PAddress, NULL, NULL);
2006  if (Process)
2007  MmDeleteRmap(OldPage, Process, PAddress);
2010 
2011  /*
2012  * Set the PTE to point to the new page
2013  */
2014  if (!NT_SUCCESS(MmCreateVirtualMapping(Process, PAddress, NewProtect, NewPage)))
2015  {
2016  DPRINT1("MmCreateVirtualMapping failed, unable to create virtual mapping, not out of memory\n");
2017  KeBugCheck(MEMORY_MANAGEMENT);
2018  }
2019 
2020  if (Process)
2021  MmInsertRmap(NewPage, Process, PAddress);
2022 
2023  DPRINT("Address 0x%p\n", Address);
2024  return STATUS_SUCCESS;
2025 }
2026 
2027 NTSTATUS
2028 NTAPI
2032  SIZE_T Length,
2033  ULONG Protect,
2034  PULONG OldProtect)
2035 {
2037  NTSTATUS Status;
2038  ULONG_PTR MaxLength;
2039 
2041  if (Length > MaxLength)
2042  Length = (ULONG)MaxLength;
2043 
2045  &MemoryArea->SectionData.RegionListHead,
2046  BaseAddress, NULL);
2047  ASSERT(Region != NULL);
2048 
2049  if ((MemoryArea->Flags & SEC_NO_CHANGE) &&
2050  Region->Protect != Protect)
2051  {
2053  }
2054 
2055  *OldProtect = Region->Protect;
2057  &MemoryArea->SectionData.RegionListHead,
2058  BaseAddress, Length, Region->Type, Protect,
2060 
2061  return Status;
2062 }
2063 
2066  PVOID Address,
2069 {
2071  PVOID RegionBaseAddress;
2073 
2075  &MemoryArea->SectionData.RegionListHead,
2076  Address, &RegionBaseAddress);
2077  if (Region == NULL)
2078  {
2079  return STATUS_UNSUCCESSFUL;
2080  }
2081 
2083  {
2084  Segment = MemoryArea->SectionData.Segment;
2085  Info->AllocationBase = (PUCHAR)MA_GetStartingAddress(MemoryArea) - Segment->Image.VirtualAddress;
2086  Info->Type = MEM_IMAGE;
2087  }
2088  else
2089  {
2090  Info->AllocationBase = (PVOID)MA_GetStartingAddress(MemoryArea);
2091  Info->Type = MEM_MAPPED;
2092  }
2093  Info->BaseAddress = RegionBaseAddress;
2094  Info->AllocationProtect = MmProtectToValue[MemoryArea->VadNode.u.VadFlags.Protection];
2095  Info->RegionSize = Region->Length;
2096  Info->State = MEM_COMMIT;
2097  Info->Protect = Region->Protect;
2098 
2100  return STATUS_SUCCESS;
2101 }
2102 
2103 VOID NTAPI
2105 {
2106  PSECTION Section = ObjectBody;
2107 
2108  /* Check if it's an ARM3, or ReactOS section */
2109  if (!MiIsRosSectionObject(Section))
2110  {
2111  MiDeleteARM3Section(ObjectBody);
2112  return;
2113  }
2114 
2115  DPRINT("MmpDeleteSection(ObjectBody %p)\n", ObjectBody);
2116  if (Section->u.Flags.Image)
2117  {
2118  PMM_IMAGE_SECTION_OBJECT ImageSectionObject = (PMM_IMAGE_SECTION_OBJECT)Section->Segment;
2119 
2120  /*
2121  * NOTE: Section->ImageSection can be NULL for short time
2122  * during the section creating. If we fail for some reason
2123  * until the image section is properly initialized we shouldn't
2124  * process further here.
2125  */
2126  if (Section->Segment == NULL)
2127  return;
2128 
2129  KIRQL OldIrql = MiAcquirePfnLock();
2130  ImageSectionObject->SectionCount--;
2131 
2132  /* We just dereference the first segment */
2133  ASSERT(ImageSectionObject->RefCount > 0);
2134  /* MmDereferenceSegmentWithLock releases PFN lock */
2135  MmDereferenceSegmentWithLock(ImageSectionObject->Segments, OldIrql);
2136  }
2137  else
2138  {
2140 
2141  /*
2142  * NOTE: Section->Segment can be NULL for short time
2143  * during the section creating.
2144  */
2145  if (Segment == NULL)
2146  return;
2147 
2148  KIRQL OldIrql = MiAcquirePfnLock();
2149  Segment->SectionCount--;
2150 
2151  /* MmDereferenceSegmentWithLock releases PFN lock */
2152  MmDereferenceSegmentWithLock(Segment, OldIrql);
2153  }
2154 }
2155 
2156 VOID NTAPI
2158  IN PVOID Object,
2161  IN ULONG SystemHandleCount)
2162 {
2163  DPRINT("MmpCloseSection(OB %p, HC %lu)\n", Object, ProcessHandleCount);
2164 }
2165 
2166 CODE_SEG("INIT")
2167 NTSTATUS
2168 NTAPI
2170 {
2171  PSECTION PhysSection;
2172  NTSTATUS Status;
2174  UNICODE_STRING Name = RTL_CONSTANT_STRING(L"\\Device\\PhysicalMemory");
2175  LARGE_INTEGER SectionSize;
2176  HANDLE Handle;
2178 
2179  /*
2180  * Create the section mapping physical memory
2181  */
2182  SectionSize.QuadPart = MmHighestPhysicalPage * PAGE_SIZE;
2184  &Name,
2186  NULL,
2187  NULL);
2188  /*
2189  * Create the Object
2190  */
2193  &Obj,
2195  NULL,
2196  sizeof(*PhysSection),
2197  0,
2198  0,
2199  (PVOID*)&PhysSection);
2200  if (!NT_SUCCESS(Status))
2201  {
2202  DPRINT1("MmCreatePhysicalMemorySection: failed to create object (0x%lx)\n", Status);
2203  return Status;
2204  }
2205 
2206  /*
2207  * Initialize it
2208  */
2209  RtlZeroMemory(PhysSection, sizeof(*PhysSection));
2210 
2211  /* Mark this as a "ROS Section" */
2212  PhysSection->u.Flags.filler = 1;
2213  PhysSection->InitialPageProtection = PAGE_EXECUTE_READWRITE;
2214  PhysSection->u.Flags.PhysicalMemory = 1;
2215  PhysSection->SizeOfSection = SectionSize;
2218  if (Segment == NULL)
2219  {
2220  ObDereferenceObject(PhysSection);
2221  return STATUS_NO_MEMORY;
2222  }
2224  PhysSection->Segment = (PSEGMENT)Segment;
2225  Segment->RefCount = 1;
2226 
2227  Segment->ReferenceCount = &Segment->RefCount;
2228  Segment->Flags = &Segment->SegFlags;
2229 
2231  Segment->Image.FileOffset = 0;
2232  Segment->Protection = PAGE_EXECUTE_READWRITE;
2233  Segment->RawLength = SectionSize;
2234  Segment->Length = SectionSize;
2235  Segment->SegFlags = MM_PHYSICALMEMORY_SEGMENT;
2236  Segment->WriteCopy = FALSE;
2237  Segment->Image.VirtualAddress = 0;
2238  Segment->Image.Characteristics = 0;
2240 
2241  Status = ObInsertObject(PhysSection,
2242  NULL,
2244  0,
2245  NULL,
2246  &Handle);
2247  if (!NT_SUCCESS(Status))
2248  {
2249  ObDereferenceObject(PhysSection);
2250  return Status;
2251  }
2253 
2254  return STATUS_SUCCESS;
2255 }
2256 
2257 CODE_SEG("INIT")
2258 NTSTATUS
2259 NTAPI
2261 {
2262  OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
2264 
2265  DPRINT("Creating Section Object Type\n");
2266 
2267  /* Initialize the section based root */
2270 
2271  /* Initialize the Section object type */
2272  RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
2273  RtlInitUnicodeString(&Name, L"Section");
2274  ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
2275  ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(SECTION);
2276  ObjectTypeInitializer.PoolType = PagedPool;
2277  ObjectTypeInitializer.UseDefaultObject = TRUE;
2278  ObjectTypeInitializer.GenericMapping = MmpSectionMapping;
2279  ObjectTypeInitializer.DeleteProcedure = MmpDeleteSection;
2280  ObjectTypeInitializer.CloseProcedure = MmpCloseSection;
2281  ObjectTypeInitializer.ValidAccessMask = SECTION_ALL_ACCESS;
2282  ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
2283  ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &MmSectionObjectType);
2284 
2286 
2287  return STATUS_SUCCESS;
2288 }
2289 
2290 static
2291 NTSTATUS
2292 NTAPI
2296  PLARGE_INTEGER UMaximumSize,
2300  BOOLEAN GotFileHandle)
2301 /*
2302  * Create a section backed by a data file
2303  */
2304 {
2305  PSECTION Section;
2306  NTSTATUS Status;
2309  KIRQL OldIrql;
2310 
2311  /*
2312  * Create the section
2313  */
2318  NULL,
2319  sizeof(*Section),
2320  0,
2321  0,
2322  (PVOID*)&Section);
2323  if (!NT_SUCCESS(Status))
2324  {
2325  return Status;
2326  }
2327  /*
2328  * Initialize it
2329  */
2330  RtlZeroMemory(Section, sizeof(*Section));
2331 
2332  /* Mark this as a "ROS" section */
2333  Section->u.Flags.filler = 1;
2334  Section->InitialPageProtection = SectionPageProtection;
2335  Section->u.Flags.File = 1;
2336 
2338  Section->u.Flags.NoChange = 1;
2340  Section->u.Flags.Reserve = 1;
2341 
2342  if (!GotFileHandle)
2343  {
2344  ASSERT(UMaximumSize != NULL);
2345  // ASSERT(UMaximumSize->QuadPart != 0);
2346  MaximumSize = *UMaximumSize;
2347  }
2348  else
2349  {
2352  if (!NT_SUCCESS(Status))
2353  {
2354  ObDereferenceObject(Section);
2355  return Status;
2356  }
2357 
2358  /*
2359  * FIXME: Revise this once a locking order for file size changes is
2360  * decided
2361  */
2362  if ((UMaximumSize != NULL) && (UMaximumSize->QuadPart != 0))
2363  {
2364  MaximumSize = *UMaximumSize;
2365  }
2366  else
2367  {
2369  /* Mapping zero-sized files isn't allowed. */
2370  if (MaximumSize.QuadPart == 0)
2371  {
2372  ObDereferenceObject(Section);
2374  }
2375  }
2376 
2377  if (MaximumSize.QuadPart > FileSize.QuadPart)
2378  {
2381  sizeof(LARGE_INTEGER),
2382  &MaximumSize);
2383  if (!NT_SUCCESS(Status))
2384  {
2385  ObDereferenceObject(Section);
2387  }
2388  }
2389  }
2390 
2391  if (FileObject->SectionObjectPointer == NULL)
2392  {
2393  ObDereferenceObject(Section);
2395  }
2396 
2397  /*
2398  * Lock the file
2399  */
2401  if (Status != STATUS_SUCCESS)
2402  {
2403  ObDereferenceObject(Section);
2404  return Status;
2405  }
2406 
2407  /* Lock the PFN lock while messing with Section Object pointers */
2408 grab_segment:
2409  OldIrql = MiAcquirePfnLock();
2410  Segment = FileObject->SectionObjectPointer->DataSectionObject;
2411 
2412  while (Segment && (Segment->SegFlags & (MM_SEGMENT_INDELETE | MM_SEGMENT_INCREATE)))
2413  {
2414  MiReleasePfnLock(OldIrql);
2416  OldIrql = MiAcquirePfnLock();
2417  Segment = FileObject->SectionObjectPointer->DataSectionObject;
2418  }
2419 
2420  /*
2421  * If this file hasn't been mapped as a data file before then allocate a
2422  * section segment to describe the data file mapping
2423  */
2424  if (Segment == NULL)
2425  {
2426  /* Release the lock. ExAllocatePoolWithTag might acquire it */
2427  MiReleasePfnLock(OldIrql);
2428 
2431  if (Segment == NULL)
2432  {
2433  //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
2434  ObDereferenceObject(Section);
2435  return STATUS_NO_MEMORY;
2436  }
2437 
2438  /* We are creating it */
2439  RtlZeroMemory(Segment, sizeof(*Segment));
2441  Segment->RefCount = 1;
2442 
2443  /* Acquire lock again */
2444  OldIrql = MiAcquirePfnLock();
2445 
2446  if (FileObject->SectionObjectPointer->DataSectionObject != NULL)
2447  {
2448  /* Well that's bad luck. Restart it all over */
2449  MiReleasePfnLock(OldIrql);
2451  goto grab_segment;
2452  }
2453 
2454  FileObject->SectionObjectPointer->DataSectionObject = Segment;
2455 
2456  /* We're safe to release the lock now */
2457  MiReleasePfnLock(OldIrql);
2458 
2459  Section->Segment = (PSEGMENT)Segment;
2460 
2461  /* Self-referencing segment */
2462  Segment->Flags = &Segment->SegFlags;
2463  Segment->ReferenceCount = &Segment->RefCount;
2464 
2465  Segment->SectionCount = 1;
2466 
2468  Segment->FileObject = FileObject;
2470 
2471  Segment->Image.FileOffset = 0;
2472  Segment->Protection = SectionPageProtection;
2473 
2474  Segment->Image.Characteristics = 0;
2477  {
2478  Segment->Length.QuadPart = Segment->RawLength.QuadPart = 0;
2479  }
2480  else
2481  {
2482  Segment->RawLength.QuadPart = MaximumSize.QuadPart;
2483  Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
2484  }
2485  Segment->Image.VirtualAddress = 0;
2487 
2488  /* We're good to use it now */
2489  OldIrql = MiAcquirePfnLock();
2490  Segment->SegFlags &= ~MM_SEGMENT_INCREATE;
2491  MiReleasePfnLock(OldIrql);
2492  }
2493  else
2494  {
2495  Section->Segment = (PSEGMENT)Segment;
2496  InterlockedIncrement64(&Segment->RefCount);
2497  InterlockedIncrementUL(&Segment->SectionCount);
2498 
2499  MiReleasePfnLock(OldIrql);
2500 
2502 
2503  if (MaximumSize.QuadPart > Segment->RawLength.QuadPart &&
2505  {
2506  Segment->RawLength.QuadPart = MaximumSize.QuadPart;
2507  Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
2508  }
2509 
2511  }
2512  Section->SizeOfSection = MaximumSize;
2513 
2514  //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
2515  *SectionObject = Section;
2516  return STATUS_SUCCESS;
2517 }
2518 
2519 /*
2520  TODO: not that great (declaring loaders statically, having to declare all of
2521  them, having to keep them extern, etc.), will fix in the future
2522 */
2524 (
2525  IN CONST VOID * FileHeader,
2526  IN SIZE_T FileHeaderSize,
2527  IN PVOID File,
2528  OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2529  OUT PULONG Flags,
2530  IN PEXEFMT_CB_READ_FILE ReadFileCb,
2531  IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
2532 );
2533 
2535 (
2536  IN CONST VOID * FileHeader,
2537  IN SIZE_T FileHeaderSize,
2538  IN PVOID File,
2539  OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2540  OUT PULONG Flags,
2541  IN PEXEFMT_CB_READ_FILE ReadFileCb,
2542  IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
2543 );
2544 
2546 {
2548 #ifdef __ELF
2550 #endif
2551 };
2552 
2553 static
2555 NTAPI
2557 {
2558  SIZE_T SizeOfSegments;
2559  PMM_SECTION_SEGMENT Segments;
2560 
2561  /* TODO: check for integer overflow */
2562  SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * NrSegments;
2563 
2565  SizeOfSegments,
2567 
2568  if(Segments)
2569  RtlZeroMemory(Segments, SizeOfSegments);
2570 
2571  return Segments;
2572 }
2573 static
2574 NTSTATUS
2575 NTAPI
2578  IN ULONG Length,
2579  OUT PVOID * Data,
2580  OUT PVOID * AllocBase,
2581  OUT PULONG ReadSize)
2582 {
2583  NTSTATUS Status;
2585  ULONG AdjustOffset;
2586  ULONG OffsetAdjustment;
2587  ULONG BufferSize;
2588  ULONG UsedSize;
2589  PVOID Buffer;
2592 
2594 
2595  if(Length == 0)
2596  {
2597  KeBugCheck(MEMORY_MANAGEMENT);
2598  }
2599 
2600  FileOffset = *Offset;
2601 
2602  /* Negative/special offset: it cannot be used in this context */
2603  if(FileOffset.u.HighPart < 0)
2604  {
2605  KeBugCheck(MEMORY_MANAGEMENT);
2606  }
2607 
2608  AdjustOffset = PAGE_ROUND_DOWN(FileOffset.u.LowPart);
2609  OffsetAdjustment = FileOffset.u.LowPart - AdjustOffset;
2610  FileOffset.u.LowPart = AdjustOffset;
2611 
2612  BufferSize = Length + OffsetAdjustment;
2614 
2615  /*
2616  * It's ok to use paged pool, because this is a temporary buffer only used in
2617  * the loading of executables. The assumption is that MmCreateSection is
2618  * always called at low IRQLs and that these buffers don't survive a brief
2619  * initialization phase
2620  */
2622  if (!Buffer)
2623  {
2625  }
2626 
2628 
2629  UsedSize = (ULONG)Iosb.Information;
2630 
2631  if(NT_SUCCESS(Status) && UsedSize < OffsetAdjustment)
2632  {
2635  }
2636 
2637  if(NT_SUCCESS(Status))
2638  {
2639  *Data = (PVOID)((ULONG_PTR)Buffer + OffsetAdjustment);
2640  *AllocBase = Buffer;
2641  *ReadSize = UsedSize - OffsetAdjustment;
2642  }
2643  else
2644  {
2645  ExFreePoolWithTag(Buffer, 'rXmM');
2646  }
2647 
2648  return Status;
2649 }
2650 
2651 #ifdef NASSERT
2652 # define MmspAssertSegmentsSorted(OBJ_) ((void)0)
2653 # define MmspAssertSegmentsNoOverlap(OBJ_) ((void)0)
2654 # define MmspAssertSegmentsPageAligned(OBJ_) ((void)0)
2655 #else
2656 static
2657 VOID
2658 NTAPI
2660 {
2661  ULONG i;
2662 
2663  for( i = 1; i < ImageSectionObject->NrSegments; ++ i )
2664  {
2665  ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >=
2666  ImageSectionObject->Segments[i - 1].Image.VirtualAddress);
2667  }
2668 }
2669 
2670 static
2671 VOID
2672 NTAPI
2674 {
2675  ULONG i;
2676 
2677  MmspAssertSegmentsSorted(ImageSectionObject);
2678 
2679  for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2680  {
2681  ASSERT(ImageSectionObject->Segments[i].Length.QuadPart > 0);
2682 
2683  if(i > 0)
2684  {
2685  ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >=
2686  (ImageSectionObject->Segments[i - 1].Image.VirtualAddress +
2687  ImageSectionObject->Segments[i - 1].Length.QuadPart));
2688  }
2689  }
2690 }
2691 
2692 static
2693 VOID
2694 NTAPI
2696 {
2697  ULONG i;
2698 
2699  for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2700  {
2701  ASSERT((ImageSectionObject->Segments[i].Image.VirtualAddress % PAGE_SIZE) == 0);
2702  ASSERT((ImageSectionObject->Segments[i].Length.QuadPart % PAGE_SIZE) == 0);
2703  }
2704 }
2705 #endif
2706 
2707 static
2708 int
2709 __cdecl
2710 MmspCompareSegments(const void * x,
2711  const void * y)
2712 {
2713  const MM_SECTION_SEGMENT *Segment1 = (const MM_SECTION_SEGMENT *)x;
2714  const MM_SECTION_SEGMENT *Segment2 = (const MM_SECTION_SEGMENT *)y;
2715 
2716  if (Segment1->Image.VirtualAddress > Segment2->Image.VirtualAddress)
2717  return 1;
2718  else if (Segment1->Image.VirtualAddress < Segment2->Image.VirtualAddress)
2719  return -1;
2720  else
2721  return 0;
2722 }
2723 
2724 /*
2725  * Ensures an image section's segments are sorted in memory
2726  */
2727 static
2728 VOID
2729 NTAPI
2731  IN ULONG Flags)
2732 {
2734  {
2735  MmspAssertSegmentsSorted(ImageSectionObject);
2736  }
2737  else
2738  {
2739  qsort(ImageSectionObject->Segments,
2740  ImageSectionObject->NrSegments,
2741  sizeof(ImageSectionObject->Segments[0]),
2743  }
2744 }
2745 
2746 
2747 /*
2748  * Ensures an image section's segments don't overlap in memory and don't have
2749  * gaps and don't have a null size. We let them map to overlapping file regions,
2750  * though - that's not necessarily an error
2751  */
2752 static
2753 BOOLEAN
2754 NTAPI
2757  IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2758  IN ULONG Flags
2759 )
2760 {
2761  ULONG i;
2762 
2764  {
2765  MmspAssertSegmentsNoOverlap(ImageSectionObject);
2766  return TRUE;
2767  }
2768 
2769  ASSERT(ImageSectionObject->NrSegments >= 1);
2770 
2771  for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2772  {
2773  if(ImageSectionObject->Segments[i].Length.QuadPart == 0)
2774  {
2775  return FALSE;
2776  }
2777 
2778  if(i > 0)
2779  {
2780  /*
2781  * TODO: relax the limitation on gaps. For example, gaps smaller than a
2782  * page could be OK (Windows seems to be OK with them), and larger gaps
2783  * could lead to image sections spanning several discontiguous regions
2784  * (NtMapViewOfSection could then refuse to map them, and they could
2785  * e.g. only be allowed as parameters to NtCreateProcess, like on UNIX)
2786  */
2787  if ((ImageSectionObject->Segments[i - 1].Image.VirtualAddress +
2788  ImageSectionObject->Segments[i - 1].Length.QuadPart) !=
2789  ImageSectionObject->Segments[i].Image.VirtualAddress)
2790  {
2791  return FALSE;
2792  }
2793  }
2794  }
2795 
2796  return TRUE;
2797 }
2798 
2799 /*
2800  * Merges and pads an image section's segments until they all are page-aligned
2801  * and have a size that is a multiple of the page size
2802  */
2803 static
2804 BOOLEAN
2805 NTAPI
2808  IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2809  IN ULONG Flags
2810 )
2811 {
2812  ULONG i;
2813  ULONG LastSegment;
2814  PMM_SECTION_SEGMENT EffectiveSegment;
2815 
2817  {
2818  MmspAssertSegmentsPageAligned(ImageSectionObject);
2819  return TRUE;
2820  }
2821 
2822  LastSegment = 0;
2823  EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
2824 
2825  for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2826  {
2827  /*
2828  * The first segment requires special handling
2829  */
2830  if (i == 0)
2831  {
2833  ULONG_PTR VirtualOffset;
2834 
2835  VirtualAddress = EffectiveSegment->Image.VirtualAddress;
2836 
2837  /* Round down the virtual address to the nearest page */
2838  EffectiveSegment->Image.VirtualAddress = PAGE_ROUND_DOWN(VirtualAddress);
2839 
2840  /* Round up the virtual size to the nearest page */
2841  EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(VirtualAddress + EffectiveSegment->Length.QuadPart) -
2842  EffectiveSegment->Image.VirtualAddress;
2843 
2844  /* Adjust the raw address and size */
2845  VirtualOffset = VirtualAddress - EffectiveSegment->Image.VirtualAddress;
2846 
2847  if (EffectiveSegment->Image.FileOffset < VirtualOffset)
2848  {
2849  return FALSE;
2850  }
2851 
2852  /*
2853  * Garbage in, garbage out: unaligned base addresses make the file
2854  * offset point in curious and odd places, but that's what we were
2855  * asked for
2856  */
2857  EffectiveSegment->Image.FileOffset -= VirtualOffset;
2858  EffectiveSegment->RawLength.QuadPart += VirtualOffset;
2859  }
2860  else
2861  {
2862  PMM_SECTION_SEGMENT Segment = &ImageSectionObject->Segments[i];
2863  ULONG_PTR EndOfEffectiveSegment;
2864 
2865  EndOfEffectiveSegment = (ULONG_PTR)(EffectiveSegment->Image.VirtualAddress + EffectiveSegment->Length.QuadPart);
2866  ASSERT((EndOfEffectiveSegment % PAGE_SIZE) == 0);
2867 
2868  /*
2869  * The current segment begins exactly where the current effective
2870  * segment ended, therefore beginning a new effective segment
2871  */
2872  if (EndOfEffectiveSegment == Segment->Image.VirtualAddress)
2873  {
2874  LastSegment ++;
2875  ASSERT(LastSegment <= i);
2876  ASSERT(LastSegment < ImageSectionObject->NrSegments);
2877 
2878  EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
2879 
2880  if (LastSegment != i)
2881  {
2882  /*
2883  * Copy the current segment. If necessary, the effective segment
2884  * will be expanded later
2885  */
2886  *EffectiveSegment = *Segment;
2887  }
2888 
2889  /*
2890  * Page-align the virtual size. We know for sure the virtual address
2891  * already is
2892  */
2893  ASSERT((EffectiveSegment->Image.VirtualAddress % PAGE_SIZE) == 0);
2894  EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(EffectiveSegment->Length.QuadPart);
2895  }
2896  /*
2897  * The current segment is still part of the current effective segment:
2898  * extend the effective segment to reflect this
2899  */
2900  else if (EndOfEffectiveSegment > Segment->Image.VirtualAddress)
2901  {
2902  static const ULONG FlagsToProtection[16] =
2903  {
2904  PAGE_NOACCESS,
2905  PAGE_READONLY,
2920  };
2921 
2922  unsigned ProtectionFlags;
2923 
2924  /*
2925  * Extend the file size
2926  */
2927 
2928  /* Unaligned segments must be contiguous within the file */
2929  if (Segment->Image.FileOffset != (EffectiveSegment->Image.FileOffset +
2930  EffectiveSegment->RawLength.QuadPart))
2931  {
2932  return FALSE;
2933  }
2934 
2935  EffectiveSegment->RawLength.QuadPart += Segment->RawLength.QuadPart;
2936 
2937  /*
2938  * Extend the virtual size
2939  */
2940  ASSERT(PAGE_ROUND_UP(Segment->Image.VirtualAddress + Segment->Length.QuadPart) >= EndOfEffectiveSegment);
2941 
2942  EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(Segment->Image.VirtualAddress + Segment->Length.QuadPart) -
2943  EffectiveSegment->Image.VirtualAddress;
2944 
2945  /*
2946  * Merge the protection
2947  */
2948  EffectiveSegment->Protection |= Segment->Protection;
2949 
2950  /* Clean up redundance */
2951  ProtectionFlags = 0;
2952 
2953  if(EffectiveSegment->Protection & PAGE_IS_READABLE)
2954  ProtectionFlags |= 1 << 0;
2955 
2956  if(EffectiveSegment->Protection & PAGE_IS_WRITABLE)
2957  ProtectionFlags |= 1 << 1;
2958 
2959  if(EffectiveSegment->Protection & PAGE_IS_EXECUTABLE)
2960  ProtectionFlags |= 1 << 2;
2961 
2962  if(EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
2963  ProtectionFlags |= 1 << 3;
2964 
2965  ASSERT(ProtectionFlags < 16);
2966  EffectiveSegment->Protection = FlagsToProtection[ProtectionFlags];
2967 
2968  /* If a segment was required to be shared and cannot, fail */
2969  if(!(Segment->Protection & PAGE_IS_WRITECOPY) &&
2970  EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
2971  {
2972  return FALSE;
2973  }
2974  }
2975  /*
2976  * We assume no holes between segments at this point
2977  */
2978  else
2979  {
2980  KeBugCheck(MEMORY_MANAGEMENT);
2981  }
2982  }
2983  }
2984  ImageSectionObject->NrSegments = LastSegment + 1;
2985 
2986  return TRUE;
2987 }
2988 
2989 NTSTATUS
2991  PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
2992 {
2994  PVOID FileHeader;
2995  PVOID FileHeaderBuffer;
2996  ULONG FileHeaderSize;
2997  ULONG Flags;
2998  ULONG OldNrSegments;
2999  NTSTATUS Status;
3000  ULONG i;
3001 
3002  /*
3003  * Read the beginning of the file (2 pages). Should be enough to contain
3004  * all (or most) of the headers
3005  */
3006  Offset.QuadPart = 0;
3007 
3009  &Offset,
3010  PAGE_SIZE * 2,
3011  &FileHeader,
3012  &FileHeaderBuffer,
3013  &FileHeaderSize);
3014 
3015  if (!NT_SUCCESS(Status))
3016  return Status;
3017 
3018  if (FileHeaderSize == 0)
3019  {
3020  ExFreePool(FileHeaderBuffer);
3021  return STATUS_UNSUCCESSFUL;
3022  }
3023 
3024  /*
3025  * Look for a loader that can handle this executable
3026  */
3027  for (i = 0; i < RTL_NUMBER_OF(ExeFmtpLoaders); ++ i)
3028  {
3029  Flags = 0;
3030 
3031  Status = ExeFmtpLoaders[i](FileHeader,
3032  FileHeaderSize,
3033  FileObject,
3034  ImageSectionObject,
3035  &Flags,
3038 
3039  if (!NT_SUCCESS(Status))
3040  {
3041  if (ImageSectionObject->Segments)
3042  {
3043  ExFreePool(ImageSectionObject->Segments);
3044  ImageSectionObject->Segments = NULL;
3045  }
3046  }
3047 
3049  break;
3050  }
3051 
3052  ExFreePoolWithTag(FileHeaderBuffer, 'rXmM');
3053 
3054  /*
3055  * No loader handled the format
3056  */
3058  {
3061  }
3062 
3063  if (!NT_SUCCESS(Status))
3064  return Status;
3065 
3066  ASSERT(ImageSectionObject->Segments != NULL);
3067  ASSERT(ImageSectionObject->RefCount > 0);
3068 
3069  /*
3070  * Some defaults
3071  */
3072  /* FIXME? are these values platform-dependent? */
3073  if (ImageSectionObject->ImageInformation.MaximumStackSize == 0)
3074  ImageSectionObject->ImageInformation.MaximumStackSize = 0x40000;
3075 
3076  if(ImageSectionObject->ImageInformation.CommittedStackSize == 0)
3077  ImageSectionObject->ImageInformation.CommittedStackSize = 0x1000;
3078 
3079  if(ImageSectionObject->BasedAddress == NULL)
3080  {
3081  if(ImageSectionObject->ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL)
3082  ImageSectionObject->BasedAddress = (PVOID)0x10000000;
3083  else
3084  ImageSectionObject->BasedAddress = (PVOID)0x00400000;
3085  }
3086 
3087  /*
3088  * And now the fun part: fixing the segments
3089  */
3090 
3091  /* Sort them by virtual address */
3092  MmspSortSegments(ImageSectionObject, Flags);
3093 
3094  /* Ensure they don't overlap in memory */
3095  if (!MmspCheckSegmentBounds(ImageSectionObject, Flags))
3097 
3098  /* Ensure they are aligned */
3099  OldNrSegments = ImageSectionObject->NrSegments;
3100 
3101  if (!MmspPageAlignSegments(ImageSectionObject, Flags))
3103 
3104  /* Trim them if the alignment phase merged some of them */
3105  if (ImageSectionObject->NrSegments < OldNrSegments)
3106  {
3107  PMM_SECTION_SEGMENT Segments;
3108  SIZE_T SizeOfSegments;
3109 
3110  SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * ImageSectionObject->NrSegments;
3111 
3112  Segments = ExAllocatePoolWithTag(PagedPool,
3113  SizeOfSegments,
3115 
3116  if (Segments == NULL)
3118 
3119  RtlCopyMemory(Segments, ImageSectionObject->Segments, SizeOfSegments);
3120  ExFreePool(ImageSectionObject->Segments);
3121  ImageSectionObject->Segments = Segments;
3122  }
3123 
3124  /* And finish their initialization */
3125  for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3126  {
3127  ExInitializeFastMutex(&ImageSectionObject->Segments[i].Lock);
3128  ImageSectionObject->Segments[i].ReferenceCount = &ImageSectionObject->RefCount;
3129  ImageSectionObject->Segments[i].Flags = &ImageSectionObject->SegFlags;
3130  MiInitializeSectionPageTable(&ImageSectionObject->Segments[i]);
3131  ImageSectionObject->Segments[i].FileObject = FileObject;
3132  }
3133 
3134  ASSERT(ImageSectionObject->RefCount > 0);
3135 
3136  ImageSectionObject->FileObject = FileObject;
3137 
3139  return Status;
3140 }
3141 
3142 NTSTATUS
3146  PLARGE_INTEGER UMaximumSize,
3150 {
3151  PSECTION Section;
3152  NTSTATUS Status;
3153  PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3154  KIRQL OldIrql;
3155 
3156 
3157  if (FileObject == NULL)
3159 
3160  if (FileObject->SectionObjectPointer == NULL)
3161  {
3162  DPRINT1("Denying section creation due to missing cache initialization\n");
3164  }
3165 
3166  /*
3167  * Create the section
3168  */
3173  NULL,
3174  sizeof(*Section),
3175  0,
3176  0,
3177  (PVOID*)(PVOID)&Section);
3178  if (!NT_SUCCESS(Status))
3179  {
3180  return Status;
3181  }
3182 
3183  /*
3184  * Initialize it
3185  */
3186  RtlZeroMemory(Section, sizeof(*Section));
3187 
3188  /* Mark this as a "ROS" Section */
3189  Section->u.Flags.filler = 1;
3190 
3191  Section->InitialPageProtection = SectionPageProtection;
3192  Section->u.Flags.File = 1;
3193  Section->u.Flags.Image = 1;
3195  Section->u.Flags.NoChange = 1;
3196 
3197 grab_image_section_object:
3198  OldIrql = MiAcquirePfnLock();
3199 
3200  /* Wait for it to be properly created or deleted */
3201  ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
3202  while(ImageSectionObject && (ImageSectionObject->SegFlags & (MM_SEGMENT_INDELETE | MM_SEGMENT_INCREATE)))
3203  {
3204  MiReleasePfnLock(OldIrql);
3205 
3207 
3208  OldIrql = MiAcquirePfnLock();
3209  ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
3210  }
3211 
3212  if (ImageSectionObject == NULL)
3213  {
3214  NTSTATUS StatusExeFmt;
3215 
3216  /* Release the lock because ExAllocatePoolWithTag could need to acquire it */
3217  MiReleasePfnLock(OldIrql);
3218 
3219  ImageSectionObject = ExAllocatePoolZero(NonPagedPool, sizeof(MM_IMAGE_SECTION_OBJECT), TAG_MM_SECTION_SEGMENT);
3220  if (ImageSectionObject == NULL)
3221  {
3222  ObDereferenceObject(Section);
3223  return STATUS_NO_MEMORY;
3224  }
3225 
3226  ImageSectionObject->SegFlags = MM_SEGMENT_INCREATE;
3227  ImageSectionObject->RefCount = 1;
3228  ImageSectionObject->SectionCount = 1;
3229 
3230  OldIrql = MiAcquirePfnLock();
3231  if (FileObject->SectionObjectPointer->ImageSectionObject != NULL)
3232  {
3233  MiReleasePfnLock(OldIrql);
3234  /* Bad luck. Start over */
3235  ExFreePoolWithTag(ImageSectionObject, TAG_MM_SECTION_SEGMENT);
3236  goto grab_image_section_object;
3237  }
3238 
3239  FileObject->SectionObjectPointer->ImageSectionObject = ImageSectionObject;
3240 
3241  MiReleasePfnLock(OldIrql);
3242 
3243  /* Purge the cache */
3244  CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, NULL);
3245 
3246  StatusExeFmt = ExeFmtpCreateImageSection(FileObject, ImageSectionObject);
3247 
3248  if (!NT_SUCCESS(StatusExeFmt))
3249  {
3250  /* Unset */
3251  OldIrql = MiAcquirePfnLock();
3252  FileObject->SectionObjectPointer->ImageSectionObject = NULL;
3253  MiReleasePfnLock(OldIrql);
3254 
3255  if(ImageSectionObject->Segments != NULL)
3256  ExFreePool(ImageSectionObject->Segments);
3257 
3258  /*
3259  * If image file is empty, then return that the file is invalid for section
3260  */
3261  Status = StatusExeFmt;
3262  if (StatusExeFmt == STATUS_END_OF_FILE)
3263  {
3265  }
3266 
3267  ExFreePoolWithTag(ImageSectionObject, TAG_MM_SECTION_SEGMENT);
3268  ObDereferenceObject(Section);
3269  return Status;
3270  }
3271 
3272  Section->Segment = (PSEGMENT)ImageSectionObject;
3273  ASSERT(ImageSectionObject->Segments);
3274  ASSERT(ImageSectionObject->RefCount > 0);
3275 
3276  /*
3277  * Lock the file
3278  */
3280  if (!NT_SUCCESS(Status))
3281  {
3282  /* Unset */
3283  OldIrql = MiAcquirePfnLock();
3284  FileObject->SectionObjectPointer->ImageSectionObject = NULL;
3285  MiReleasePfnLock(OldIrql);
3286 
3287  ExFreePool(ImageSectionObject->Segments);
3288  ExFreePool(ImageSectionObject);
3289  ObDereferenceObject(Section);
3290  return Status;
3291  }
3292 
3293  OldIrql = MiAcquirePfnLock();
3294  ImageSectionObject->SegFlags &= ~MM_SEGMENT_INCREATE;
3295 
3296  /* Take a ref on the file on behalf of the newly created structure */
3298 
3299  MiReleasePfnLock(OldIrql);
3300 
3301  Status = StatusExeFmt;
3302  }
3303  else
3304  {
3305  /* If FS driver called for delete, tell them it's not possible anymore. */
3306  ImageSectionObject->SegFlags &= ~MM_IMAGE_SECTION_FLUSH_DELETE;
3307 
3308  /* Take one ref */
3309  InterlockedIncrement64(&ImageSectionObject->RefCount);
3310  ImageSectionObject->SectionCount++;
3311 
3312  MiReleasePfnLock(OldIrql);
3313 
3314  Section->Segment = (PSEGMENT)ImageSectionObject;
3315 
3317  }
3318  //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
3319  *SectionObject = Section;
3320  ASSERT(ImageSectionObject->RefCount > 0);
3321 
3322  return Status;
3323 }
3324 
3325 
3326 
3327 static NTSTATUS
3330  BOOLEAN AsImage,
3332  PVOID* BaseAddress,
3333  SIZE_T ViewSize,
3334  ULONG Protect,
3335  LONGLONG ViewOffset,
3337 {
3338  PMEMORY_AREA MArea;
3339  NTSTATUS Status;
3340  ULONG Granularity;
3341 
3342  ASSERT(ViewSize != 0);
3343 
3344  if (Segment->WriteCopy)
3345  {
3346  /* We have to do this because the not present fault
3347  * and access fault handlers depend on the protection
3348  * that should be granted AFTER the COW fault takes
3349  * place to be in Region->Protect. The not present fault
3350  * handler changes this to the correct protection for COW when
3351  * mapping the pages into the process's address space. If a COW
3352  * fault takes place, the access fault handler sets the page protection
3353  * to these values for the newly copied pages
3354  */
3355  if (Protect == PAGE_WRITECOPY)
3357  else if (Protect == PAGE_EXECUTE_WRITECOPY)
3359  }
3360 
3361  if (*BaseAddress == NULL)
3362  Granularity = MM_ALLOCATION_GRANULARITY;
3363  else
3364  Granularity = PAGE_SIZE;
3365 
3366 #ifdef NEWCC
3367  if (Segment->Flags & MM_DATAFILE_SEGMENT)
3368  {
3370  FileOffset.QuadPart = ViewOffset;
3371  ObReferenceObject(Section);
3373  }
3374 #endif
3377  BaseAddress,
3378  ViewSize,
3379  Protect,
3380  &MArea,
3382  Granularity);
3383  if (!NT_SUCCESS(Status))
3384  {
3385  DPRINT1("Mapping between 0x%p and 0x%p failed (%X).\n",
3386  (*BaseAddress), (char*)(*BaseAddress) + ViewSize, Status);
3387  return Status;
3388  }
3389 
3390  InterlockedIncrement64(Segment->ReferenceCount);
3391 
3392  MArea->SectionData.Segment = Segment;
3393  MArea->SectionData.ViewOffset = ViewOffset;
3394  if (AsImage)
3395  {
3397  }
3398 
3399  MmInitializeRegion(&MArea->SectionData.RegionListHead,
3400  ViewSize, 0, Protect);
3401 
3402  return STATUS_SUCCESS;
3403 }
3404 
3405 
3406 static VOID
3408  PFN_NUMBER Page, SWAPENTRY SwapEntry, BOOLEAN Dirty)
3409 {
3410  ULONG_PTR Entry;
3412  SWAPENTRY SavedSwapEntry;
3416 
3419 
3421 
3423  MemoryArea->SectionData.ViewOffset;
3424 
3425  Segment = MemoryArea->SectionData.Segment;
3426 
3428  while (Entry && MM_IS_WAIT_PTE(Entry))
3429  {
3432 
3433  YieldProcessor();
3434 
3438  }
3439 
3440  /*
3441  * For a dirty, datafile, non-private page, there shoulkd be no swap entry
3442  */
3443  if (*Segment->Flags & MM_DATAFILE_SEGMENT)
3444  {
3445  if (Page == PFN_FROM_SSE(Entry) && Dirty)
3446  {
3447  ASSERT(SwapEntry == 0);
3448  }
3449  }
3450 
3451  if (SwapEntry != 0)
3452  {
3453  /*
3454  * Sanity check
3455  */
3456  MmFreeSwapPage(SwapEntry);
3457  }
3458  else if (Page != 0)
3459  {
3460  if (IS_SWAP_FROM_SSE(Entry) ||
3461  Page != PFN_FROM_SSE(Entry))
3462  {
3463  ASSERT(Process != NULL);
3464 
3465  /*
3466  * Just dereference private pages
3467  */
3468  SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
3469  if (SavedSwapEntry != 0)
3470  {
3471  MmFreeSwapPage(SavedSwapEntry);
3473  }
3476  }
3477  else
3478  {
3479  if (Process)
3480  {
3482  }
3483 
3484  /* We don't dirtify for System Space Maps. We let Cc manage that */
3486  }
3487  }
3488 }
3489 
3490 static NTSTATUS
3493 {
3494  NTSTATUS Status;
3497  PLIST_ENTRY CurrentEntry;
3498  PMM_REGION CurrentRegion;
3499  PLIST_ENTRY RegionListHead;
3500 
3502  BaseAddress);
3503  if (MemoryArea == NULL)
3504  {
3505  return STATUS_UNSUCCESSFUL;
3506  }
3507 
3508  Segment = MemoryArea->SectionData.Segment;
3509 
3510 #ifdef NEWCC
3511  if (Segment->Flags & MM_DATAFILE_SEGMENT)
3512  {
3516 
3517  return Status;
3518  }
3519 #endif
3520 
3522 
3524 
3525  RegionListHead = &MemoryArea->SectionData.RegionListHead;
3526  while (!IsListEmpty(RegionListHead))
3527  {
3528  CurrentEntry = RemoveHeadList(RegionListHead);
3529  CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION, RegionListEntry);
3530  ExFreePoolWithTag(CurrentRegion, TAG_MM_REGION);
3531  }
3532 
3533  if ((*Segment->Flags) & MM_PHYSICALMEMORY_SEGMENT)
3534  {
3536  MemoryArea,
3537  NULL,
3538  NULL);
3539  }
3540  else
3541  {
3543  MemoryArea,
3545  AddressSpace);
3546  }
3548  MmDereferenceSegment(Segment);
3549  return Status;
3550 }
3551 
3552 /* This functions must be called with a locked address space */
3553 NTSTATUS
3554 NTAPI
3557  IN BOOLEAN SkipDebuggerNotify)
3558 {
3559  NTSTATUS Status;
3562  PVOID ImageBaseAddress = 0;
3563 
3564  DPRINT("Opening memory area Process %p BaseAddress %p\n",
3565  Process, BaseAddress);
3566 
3567  ASSERT(Process);
3568 
3570 
3572  BaseAddress);
3573  if (MemoryArea == NULL ||
3574 #ifdef NEWCC
3575  ((MemoryArea->Type != MEMORY_AREA_SECTION_VIEW) && (MemoryArea->Type != MEMORY_AREA_CACHE)) ||
3576 #else
3578 #endif
3580 
3581  {
3583 
3584  DPRINT1("Unable to find memory area at address %p.\n", BaseAddress);
3585  return STATUS_NOT_MAPPED_VIEW;
3586  }
3587 
3589  {
3590  ULONG i;
3591  ULONG NrSegments;
3592  PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3593  PMM_SECTION_SEGMENT SectionSegments;
3595 
3596  Segment = MemoryArea->SectionData.Segment;
3597  ImageSectionObject = ImageSectionObjectFromSegment(Segment);
3598  SectionSegments = ImageSectionObject->Segments;
3599  NrSegments = ImageSectionObject->NrSegments;
3600 
3602 
3603  /* Search for the current segment within the section segments
3604  * and calculate the image base address */
3605  for (i = 0; i < NrSegments; i++)
3606  {
3607  if (Segment == &SectionSegments[i])
3608  {
3609  ImageBaseAddress = (char*)BaseAddress - (ULONG_PTR)SectionSegments[i].Image.VirtualAddress;
3610  break;
3611  }
3612  }
3613  if (i >= NrSegments)
3614  {
3615  KeBugCheck(MEMORY_MANAGEMENT);
3616  }
3617 
3618  for (i = 0; i < NrSegments; i++)
3619  {
3620  PVOID SBaseAddress = (PVOID)
3621  ((char*)ImageBaseAddress + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
3622 
3623  Status = MmUnmapViewOfSegment(AddressSpace, SBaseAddress);
3624  if (!NT_SUCCESS(Status))
3625  {
3626  DPRINT1("MmUnmapViewOfSegment failed for %p (Process %p) with %lx\n",
3627  SBaseAddress, Process, Status);
3629  }
3630  }
3631  DPRINT("One mapping less for %p\n", ImageSectionObject->FileObject->SectionObjectPointer);
3632  InterlockedDecrement(&ImageSectionObject->MapCount);
3633  }
3634  else
3635  {
3637  if (!NT_SUCCESS(Status))
3638  {
3639  DPRINT1("MmUnmapViewOfSegment failed for %p (Process %p) with %lx\n",
3642  }
3643  }
3644 
3645  /* Notify debugger */
3646  if (ImageBaseAddress && !SkipDebuggerNotify) DbgkUnMapViewOfSection(ImageBaseAddress);
3647 
3648  return STATUS_SUCCESS;
3649 }
3650 
3651 
3652 
3653 
3676 NTSTATUS
3677 NTAPI
3679  _In_ HANDLE SectionHandle,
3680  _In_ SECTION_INFORMATION_CLASS SectionInformationClass,
3681  _Out_ PVOID SectionInformation,
3682  _In_ SIZE_T SectionInformationLength,
3684 {
3685  PSECTION Section;
3687  NTSTATUS Status;
3688  PAGED_CODE();
3689 
3691  if (PreviousMode != KernelMode)
3692  {
3693  _SEH2_TRY
3694  {
3695  ProbeForWrite(SectionInformation,
3696  SectionInformationLength,
3697  __alignof(ULONG));
3698  if (ResultLength != NULL)
3699  {
3701  sizeof(*ResultLength),
3702  __alignof(SIZE_T));
3703  }
3704  }
3706  {
3708  }
3709  _SEH2_END;
3710  }
3711 
3712  if (SectionInformationClass == SectionBasicInformation)
3713  {
3714  if (SectionInformationLength < sizeof(SECTION_BASIC_INFORMATION))
3715  {
3717  }
3718  }
3719  else if (SectionInformationClass == SectionImageInformation)
3720  {
3721  if (SectionInformationLength < sizeof(SECTION_IMAGE_INFORMATION))
3722  {
3724  }
3725  }
3726  else
3727  {
3729  }
3730 
3731  Status = ObReferenceObjectByHandle(SectionHandle,
3732  SECTION_QUERY,
3734  PreviousMode,
3735  (PVOID*)(PVOID)&Section,
3736  NULL);
3737  if (!NT_SUCCESS(Status))
3738  {
3739  DPRINT1("Failed to reference section: 0x%lx\n", Status);
3740  return Status;
3741  }
3742 
3743  switch(SectionInformationClass)
3744  {
3746  {
3748 
3749  Sbi.Size = Section->SizeOfSection;
3750  Sbi.BaseAddress = (PVOID)Section->Address.StartingVpn;
3751 
3752  Sbi.Attributes = 0;
3753  if (Section->u.Flags.File)
3754  Sbi.Attributes |= SEC_FILE;
3755  if (Section->u.Flags.Image)
3756  Sbi.Attributes |= SEC_IMAGE;
3757 
3758  /* Those are not set *************
3759  if (Section->u.Flags.Commit)
3760  Sbi.Attributes |= SEC_COMMIT;
3761  if (Section->u.Flags.Reserve)
3762  Sbi.Attributes |= SEC_RESERVE;
3763  **********************************/
3764 
3765  if (Section->u.Flags.Image)
3766  {
3767  if (MiIsRosSectionObject(Section))
3768  {
3769  PMM_IMAGE_SECTION_OBJECT ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
3770  Sbi.BaseAddress = 0;
3771  Sbi.Size.QuadPart = ImageSectionObject->ImageInformation.ImageFileSize;
3772  }
3773  else
3774  {
3775  /* Not supported yet */
3776  ASSERT(FALSE);
3777  }
3778  }
3779  else if (MiIsRosSectionObject(Section))
3780  {
3781  Sbi.BaseAddress = (PVOID)((PMM_SECTION_SEGMENT)Section->Segment)->Image.VirtualAddress;
3782  Sbi.Size.QuadPart = ((PMM_SECTION_SEGMENT)Section->Segment)->RawLength.QuadPart;
3783  }
3784  else
3785  {
3786  DPRINT1("Unimplemented code path!");
3787  }
3788 
3789  _SEH2_TRY
3790  {
3791  *((SECTION_BASIC_INFORMATION*)SectionInformation) = Sbi;
3792  if (ResultLength != NULL)
3793  {
3794  *ResultLength = sizeof(Sbi);
3795  }
3796  }
3798  {
3800  }
3801  _SEH2_END;
3802  break;
3803  }
3805  {
3806  if (!Section->u.Flags.Image)
3807  {
3809  }
3810  else if (MiIsRosSectionObject(Section))
3811  {
3812  PMM_IMAGE_SECTION_OBJECT ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
3813 
3814  _SEH2_TRY
3815  {
3816  PSECTION_IMAGE_INFORMATION Sii = (PSECTION_IMAGE_INFORMATION)SectionInformation;
3817  *Sii = ImageSectionObject->ImageInformation;
3818  if (ResultLength != NULL)
3819  {
3820  *ResultLength = sizeof(*Sii);
3821  }
3822  }
3824  {
3826  }
3827  _SEH2_END;
3828  }
3829  else
3830  {
3831  _SEH2_TRY
3832  {
3833  PSECTION_IMAGE_INFORMATION Sii = (PSECTION_IMAGE_INFORMATION)SectionInformation;
3834  *Sii = *Section->Segment->u2.ImageInformation;
3835  if (ResultLength != NULL)
3836  *ResultLength = sizeof(*Sii);
3837  }
3839  {
3841  }
3842  _SEH2_END;
3843  }
3844  break;
3845  }
3846  default:
3847  DPRINT1("Unknown SectionInformationClass: %d\n", SectionInformationClass);
3849  }
3850 
3851  ObDereferenceObject(Section);
3852 
3853  return Status;
3854 }
3855 
3856 /**********************************************************************
3857  * NAME EXPORTED
3858  * MmMapViewOfSection
3859  *
3860  * DESCRIPTION
3861  * Maps a view of a section into the virtual address space of a
3862  * process.
3863  *
3864  * ARGUMENTS
3865  * Section
3866  * Pointer to the section object.
3867  *
3868  * ProcessHandle
3869  * Pointer to the process.
3870  *
3871  * BaseAddress
3872  * Desired base address (or NULL) on entry;
3873  * Actual base address of the view on exit.
3874  *
3875  * ZeroBits
3876  * Number of high order address bits that must be zero.
3877  *
3878  * CommitSize
3879  * Size in bytes of the initially committed section of
3880  * the view.
3881  *
3882  * SectionOffset
3883  * Offset in bytes from the beginning of the section
3884  * to the beginning of the view.
3885  *
3886  * ViewSize
3887  * Desired length of map (or zero to map all) on entry
3888  * Actual length mapped on exit.
3889  *
3890  * InheritDisposition
3891  * Specified how the view is to be shared with
3892  * child processes.
3893  *
3894  * AllocationType
3895  * Type of allocation for the pages.
3896  *
3897  * Protect
3898  * Protection for the committed region of the view.
3899  *
3900  * RETURN VALUE
3901  * Status.
3902  *
3903  * @implemented
3904  */
3915  IN ULONG Protect)
3916 {
3917  PSECTION Section;
3920  BOOLEAN NotAtBase = FALSE;
3921 
3923  {
3924  DPRINT("Mapping ARM3 section into %s\n", Process->ImageFileName);
3926  Process,
3927  BaseAddress,
3928  ZeroBits,
3929  CommitSize,
3930  SectionOffset,
3931  ViewSize,
3934  Protect);
3935  }
3936 
3937  ASSERT(Process);
3938 
3940  {
3942  }
3943 
3944  /* FIXME: We should keep this, but it would break code checking equality */
3945  Protect &= ~PAGE_NOCACHE;
3946 
3947  Section = SectionObject;
3948  AddressSpace = &Process->Vm;
3949 
3950  if (Section->u.Flags.NoChange)
3952 
3954 
3955  if (Section->u.Flags.Image)
3956  {
3957  ULONG i;
3958  ULONG NrSegments;
3959  ULONG_PTR ImageBase;
3960  SIZE_T ImageSize;
3961  PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3962  PMM_SECTION_SEGMENT SectionSegments;
3963 
3964  ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
3965  SectionSegments = ImageSectionObject->Segments;
3966  NrSegments = ImageSectionObject->NrSegments;
3967 
3968  ASSERT(ImageSectionObject->RefCount > 0);
3969 
3970  ImageBase = (ULONG_PTR)*BaseAddress;
3971  if (ImageBase == 0)
3972  {
3973  ImageBase = (ULONG_PTR)ImageSectionObject->BasedAddress;
3974  }
3975 
3976  ImageSize = 0;
3977  for (i = 0; i < NrSegments; i++)
3978  {
3979  ULONG_PTR MaxExtent;
3980  MaxExtent = (ULONG_PTR)(SectionSegments[i].Image.VirtualAddress +
3981  SectionSegments[i].Length.QuadPart);
3982  ImageSize = max(ImageSize, MaxExtent);
3983  }
3984 
3985  ImageSectionObject->ImageInformation.ImageFileSize = (ULONG)ImageSize;
3986 
3987  /* Check for an illegal base address */
3988  if (((ImageBase + ImageSize) > (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS) ||
3989  ((ImageBase + ImageSize) < ImageSize))
3990  {
3991  ASSERT(*BaseAddress == NULL);
3992  ImageBase = ALIGN_DOWN_BY((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - ImageSize,
3994  NotAtBase = TRUE;
3995  }
3996  else if (ImageBase != ALIGN_DOWN_BY(ImageBase, MM_VIRTMEM_GRANULARITY))
3997  {
3998  ASSERT(*BaseAddress == NULL);
3999  ImageBase = ALIGN_DOWN_BY(ImageBase, MM_VIRTMEM_GRANULARITY);
4000  NotAtBase = TRUE;
4001  }
4002 
4003  /* Check there is enough space to map the section at that point. */
4005  PAGE_ROUND_UP(ImageSize)) != NULL)
4006  {
4007  /* Fail if the user requested a fixed base address. */
4008  if ((*BaseAddress) != NULL)
4009  {
4012  }
4013  /* Otherwise find a gap to map the image. */
4015  if (ImageBase == 0)
4016  {
4019  }
4020  /* Remember that we loaded image at a different base address */
4021  NotAtBase = TRUE;
4022  }
4023 
4024  for (i = 0; i < NrSegments; i++)
4025  {
4026  PVOID SBaseAddress = (PVOID)
4027  ((char*)ImageBase + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
4028  MmLockSectionSegment(&SectionSegments[i]);
4030  TRUE,
4031  &SectionSegments[i],
4032  &SBaseAddress,
4033  SectionSegments[i].Length.QuadPart,
4034  SectionSegments[i].Protection,
4035  0,
4036  0);
4037  MmUnlockSectionSegment(&SectionSegments[i]);
4038  if (!NT_SUCCESS(Status))
4039  {
4040  /* roll-back */
4041  while (i--)
4042  {
4043  SBaseAddress = ((char*)ImageBase + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
4044  MmLockSectionSegment(&SectionSegments[i]);
4045  MmUnmapViewOfSegment(AddressSpace, SBaseAddress);
4046  MmUnlockSectionSegment(&SectionSegments[i]);
4047  }
4048 
4050  return Status;
4051  }
4052  }
4053 
4054  *BaseAddress = (PVOID)ImageBase;
4055  *ViewSize = ImageSize;
4056 
4057  DPRINT("Mapped %p for section pointer %p\n", ImageSectionObject, ImageSectionObject->FileObject->SectionObjectPointer);
4058 
4059  /* One more map */
4060  InterlockedIncrement(&ImageSectionObject->MapCount);
4061  }
4062  else
4063  {
4065  LONGLONG ViewOffset;
4066 
4067  ASSERT(Segment->RefCount > 0);
4068 
4069  /* check for write access */
4072  {
4075  }
4076  /* check for read access */
4079  {
4082  }
4083  /* check for execute access */
4086  {
4089  }
4090 
4091  if (SectionOffset == NULL)
4092  {
4093  ViewOffset = 0;
4094  }
4095  else
4096  {
4097  ViewOffset = SectionOffset->QuadPart;
4098  }
4099 
4100  if ((ViewOffset % PAGE_SIZE) != 0)
4101  {
4103  return STATUS_MAPPED_ALIGNMENT;
4104  }
4105 
4106  if ((*ViewSize) == 0)
4107  {
4108  (*ViewSize) = Section->SizeOfSection.QuadPart - ViewOffset;
4109  }
4110  else if ((ExGetPreviousMode() == UserMode) &&
4111  (((*ViewSize)+ViewOffset) > Section->SizeOfSection.QuadPart) &&
4112  (!Section->u.Flags.Reserve))
4113  {
4114  /* Dubious */
4115  (*ViewSize) = MIN(Section->SizeOfSection.QuadPart - ViewOffset, SIZE_T_MAX - PAGE_SIZE);
4116  }
4117 
4119 
4122  FALSE,
4123  Segment,
4124  BaseAddress,
4125  *ViewSize,
4126  Protect,
4127  ViewOffset,
4130  if (!NT_SUCCESS(Status))
4131  {
4133  return Status;
4134  }
4135  }
4136 
4138 
4139  if (NotAtBase)
4141  else
4143 
4144  return Status;
4145 }
4146 
4147 /*
4148  * @unimplemented
4149  */
4150 BOOLEAN NTAPI
4153 {
4154  BOOLEAN Ret;
4156 
4157  /* Check whether an ImageSectionObject exists */
4158  if (SectionObjectPointer->ImageSectionObject != NULL)
4159  {
4160  DPRINT1("ERROR: File can't be truncated because it has an image section\n");
4161  return FALSE;
4162  }
4163 
4165  if (!Segment)
4166  {
4167  /* There is no data section. It's fine to do anything. */
4168  return TRUE;
4169  }
4170 
4172  if ((Segment->SectionCount == 0) ||
4173  ((Segment->SectionCount == 1) && (SectionObjectPointer->SharedCacheMap != NULL)))
4174  {
4175  /* If the cache is the only one holding a reference to the segment, then it's fine to resize */
4176  Ret = TRUE;
4177  }
4178  else
4179  {
4180  /* We can't shrink, but we can extend */
4181  Ret = NewFileSize->QuadPart >= Segment->RawLength.QuadPart;
4182 #if DBG
4183  if (!Ret)
4184  {
4185  DPRINT1("Cannot truncate data: New Size %I64d, Segment Size %I64d\n", NewFileSize->QuadPart, Segment->RawLength.QuadPart);
4186  }
4187 #endif
4188  }
4190  MmDereferenceSegment(Segment);
4191 
4192  DPRINT("FIXME: didn't check for outstanding write probes\n");
4193 
4194  return Ret;
4195 }
4196 
4197 static
4198 BOOLEAN
4200 {
4202 
4204 
4205  /* Loop over all entries */
4206  for (PageTable = RtlEnumerateGenericTable(&Segment->PageTable, TRUE);
4207  PageTable != NULL;
4209  {
4210  for (ULONG i = 0; i < _countof(PageTable->PageEntries); i++)
4211  {
4212  ULONG_PTR Entry = PageTable->PageEntries[i];
4214 
4215  if (!Entry)
4216  continue;
4217 
4219  {
4220  /* I/O ongoing or swap entry. Someone mapped this file as we were not looking */
4222  return FALSE;
4223  }
4224 
4225  /* Regular entry */
4228 
4229  /* Properly remove using the used API */
4230  Offset.QuadPart = PageTable->FileOffset.QuadPart + (i << PAGE_SHIFT);
4233  }
4234  }
4235 
4237 
4238  return TRUE;
4239 }
4240 
4241 /*
4242  * @implemented
4243  */
4244 BOOLEAN NTAPI
4247 {
4248  switch(FlushType)
4249  {
4250  case MmFlushForDelete:
4251  {
4252  /*
4253  * FIXME: Check for outstanding write probes on Data section.
4254  * How do we do that ?
4255  */
4256  }
4257  /* Fall-through */
4258  case MmFlushForWrite:
4259  {
4260  KIRQL OldIrql = MiAcquirePfnLock();
4261  PMM_IMAGE_SECTION_OBJECT ImageSectionObject = SectionObjectPointer->ImageSectionObject;
4262 
4263  DPRINT("Deleting or modifying %p\n", SectionObjectPointer);
4264 
4265  /* Wait for concurrent creation or deletion of image to be done */
4266  ImageSectionObject = SectionObjectPointer->ImageSectionObject;
4267  while (ImageSectionObject && (ImageSectionObject->SegFlags & (MM_SEGMENT_INCREATE | MM_SEGMENT_INDELETE)))
4268  {
4269  MiReleasePfnLock(OldIrql);
4271  OldIrql = MiAcquirePfnLock();
4272  ImageSectionObject = SectionObjectPointer->ImageSectionObject;
4273  }
4274 
4275  if (!ImageSectionObject)
4276  {
4277  DPRINT("No image section object. Accepting\n");
4278  /* Nothing to do */
4279  MiReleasePfnLock(OldIrql);
4280  return TRUE;
4281  }
4282 
4283  /* Do we have open sections or mappings on it ? */
4284  if ((ImageSectionObject->SectionCount) || (ImageSectionObject->MapCount))
4285  {
4286  /* We do. No way to delete it */
4287  MiReleasePfnLock(OldIrql);
4288  DPRINT("Denying. There are mappings open\n");
4289  return FALSE;
4290  }
4291 
4292  /* There are no sections open on it, but we must still have pages around. Discard everything */
4293  ImageSectionObject->SegFlags |= MM_IMAGE_SECTION_FLUSH_DELETE;
4294  InterlockedIncrement64(&ImageSectionObject->RefCount);
4295  MiReleasePfnLock(OldIrql);
4296 
4297  DPRINT("Purging\n");
4298 
4299  for (ULONG i = 0; i < ImageSectionObject->NrSegments; i++)
4300  {
4301  if (!MiPurgeImageSegment(&ImageSectionObject->Segments[i]))
4302  break;
4303  }
4304 
4305  /* Grab lock again */
4306  OldIrql = MiAcquirePfnLock();
4307 
4308  if (!(ImageSectionObject->SegFlags & MM_IMAGE_SECTION_FLUSH_DELETE))
4309  {
4310  /*
4311  * Someone actually created a section while we were not looking.
4312  * Drop our ref and deny.
4313  * MmDereferenceSegmentWithLock releases Pfn lock
4314  */
4315  MmDereferenceSegmentWithLock(&ImageSectionObject->Segments[0], OldIrql);
4316  return FALSE;
4317  }
4318 
4319  /* We should be the last one holding a ref here. */
4320  ASSERT(ImageSectionObject->RefCount == 1);
4321  ASSERT(ImageSectionObject->SectionCount == 0);
4322 
4323  /* Dereference the first segment, this will free everything & release the lock */
4324  MmDereferenceSegmentWithLock(&ImageSectionObject->Segments[0], OldIrql);
4325  return TRUE;
4326  }
4327  }
4328  return FALSE;
4329 }
4330 
4331 /*
4332  * @implemented
4333  */
4334 NTSTATUS
4335 NTAPI
4337  OUT PVOID * MappedBase,
4339 {
4341 
4342  SectionOffset.QuadPart = 0;
4343 
4345 }
4346 
4347 NTSTATUS
4348 NTAPI
4355  )
4356 {
4357  PSECTION Section = SectionObject;
4360  NTSTATUS Status;
4361 
4363 
4364  PAGED_CODE();
4365 
4367  {
4369  &MmSession,
4370  MappedBase,
4371  ViewSize,
4372  SectionOffset);
4373  }
4374 
4375  DPRINT("MmMapViewInSystemSpaceEx() called\n");
4376 
4377  /* unsupported for now */
4378  ASSERT(Section->u.Flags.Image == 0);
4379 
4380  Section = SectionObject;
4381  Segment = (PMM_SECTION_SEGMENT)Section->Segment;
4382 
4383  if (*ViewSize == 0)
4384  {
4385  LONGLONG MapSizeLL;
4386 
4387  /* Page-align the mapping */
4388  SectionOffset->LowPart = PAGE_ROUND_DOWN(SectionOffset->LowPart);
4389 
4390  if (!NT_SUCCESS(RtlLongLongSub(Section->SizeOfSection.QuadPart, SectionOffset->QuadPart, &MapSizeLL)))
4391  return STATUS_INVALID_VIEW_SIZE;
4392 
4393  if (!NT_SUCCESS(RtlLongLongToSIZET(MapSizeLL, ViewSize)))
4394  return STATUS_INVALID_VIEW_SIZE;
4395  }
4396  else
4397  {
4398  LONGLONG HelperLL;
4399 
4400  /* Get the map end */
4401  if (!NT_SUCCESS(RtlLongLongAdd(SectionOffset->QuadPart, *ViewSize, &HelperLL)))
4402  return STATUS_INVALID_VIEW_SIZE;
4403 
4404  /* Round it up, if needed */
4405  if (HelperLL % PAGE_SIZE)
4406  {
4407  if (!NT_SUCCESS(RtlLongLongAdd(HelperLL, PAGE_SIZE - (HelperLL % PAGE_SIZE), &HelperLL)))
4408  return STATUS_INVALID_VIEW_SIZE;
4409  }
4410 
4411  /* Now that we have the mapping end, we can align down its start */
4412  SectionOffset->LowPart = PAGE_ROUND_DOWN(SectionOffset->LowPart);
4413 
4414  /* Get the new size */
4415  if (!NT_SUCCESS(RtlLongLongSub(HelperLL, SectionOffset->QuadPart, &HelperLL)))
4416  return STATUS_INVALID_VIEW_SIZE;
4417 
4418  if (!NT_SUCCESS(RtlLongLongToSIZET(HelperLL, ViewSize)))
4419  return STATUS_INVALID_VIEW_SIZE;
4420  }
4421 
4423 
4425 
4427 
4429  Section->u.Flags.Image,
4430  Segment,
4431  MappedBase,
4432  *ViewSize,
4434  SectionOffset->QuadPart,
4435  SEC_RESERVE);
4436 
4439 
4440  return Status;
4441 }
4442 
4443 /* This function must be called with adress space lock held */
4444 NTSTATUS
4445 NTAPI
4447 {
4448  DPRINT("MmUnmapViewInSystemSpace() called\n");
4449 
4451 }
4452 
4453 /**********************************************************************
4454  * NAME EXPORTED
4455  * MmCreateSection@
4456  *
4457  * DESCRIPTION
4458  * Creates a section object.
4459  *
4460  * ARGUMENTS
4461  * SectionObject (OUT)
4462  * Caller supplied storage for the resulting pointer
4463  * to a SECTION_OBJECT instance;
4464  *
4465  * DesiredAccess
4466  * Specifies the desired access to the section can be a
4467  * combination of:
4468  * STANDARD_RIGHTS_REQUIRED |
4469  * SECTION_QUERY |
4470  * SECTION_MAP_WRITE |
4471  * SECTION_MAP_READ |
4472  * SECTION_MAP_EXECUTE
4473  *
4474  * ObjectAttributes [OPTIONAL]
4475  * Initialized attributes for the object can be used
4476  * to create a named section;
4477  *
4478  * MaximumSize
4479  * Maximizes the size of the memory section. Must be
4480  * non-NULL for a page-file backed section.
4481  * If value specified for a mapped file and the file is
4482  * not large enough, file will be extended.
4483  *
4484  * SectionPageProtection
4485  * Can be a combination of:
4486  * PAGE_READONLY |
4487  * PAGE_READWRITE |
4488  * PAGE_WRITEONLY |
4489  * PAGE_WRITECOPY
4490  *
4491  * AllocationAttributes
4492  * Can be a combination of:
4493  * SEC_IMAGE |
4494  * SEC_RESERVE
4495  *
4496  * FileHandle
4497  * Handle to a file to create a section mapped to a file
4498  * instead of a memory backed section;
4499  *
4500  * File
4501  * Unknown.
4502  *
4503  * RETURN VALUE
4504  * Status.
4505  *
4506  * @implemented
4507  */
4517 {
4518  NTSTATUS Status;
4519  ULONG Protection;
4520  PSECTION *SectionObject = (PSECTION *)Section;
4521  BOOLEAN FileLock = FALSE;
4522 
4523  /* Check if an ARM3 section is being created instead */
4525  {
4526  if (!(FileObject) && !(FileHandle))
4527  {
4528  return MmCreateArm3Section(Section,
4529  DesiredAccess,
4531  MaximumSize,
4533  AllocationAttributes &~ 1,
4534  FileHandle,
4535  FileObject);
4536  }
4537  }
4538 
4539  /* Convert section flag to page flag */
4541 
4542  /* Check to make sure the protection is correct. Nt* does this already */
4544  if (Protection == MM_INVALID_PROTECTION)
4545  {
4546  DPRINT1("Page protection is invalid\n");
4548  }
4549 
4550  /* Check if this is going to be a data or image backed file section */
4551  if ((FileHandle) || (FileObject))
4552  {
4553  /* These cannot be mapped with large pages */
4555  {
4556  DPRINT1("Large pages cannot be used with an image mapping\n");
4558  }
4559 
4560  /* Did the caller pass a file object ? */
4561  if (FileObject)
4562  {
4563  /* Reference the object directly */
4565 
4566  /* We don't create image mappings with file objects */
4568  }
4569  else
4570  {
4571  /* Reference the file handle to get the object */
4573  MmMakeFileAccess[Protection],
4576  (PVOID*)&FileObject,
4577  NULL);
4578  if (!NT_SUCCESS(Status))
4579  {
4580  DPRINT1("Failed to get a handle to the FO: %lx\n", Status);
4581  return Status;
4582  }
4583 
4584  /* Lock the file */
4586  if (!NT_SUCCESS(Status))
4587  {
4589  return Status;
4590  }
4591 
4592  FileLock = TRUE;
4593 
4594  /* Deny access if there are writes on the file */
4595 #if 0
4597  {
4598  DPRINT1("Cannot create image maps with writers open on the file!\n");
4600  goto Quit;
4601  }
4602 #else
4604  DPRINT1("Creating image map with writers open on the file!\n");
4605 #endif
4606  }
4607  }
4608  else
4609  {
4610  /* A handle must be supplied with SEC_IMAGE, as this is the no-handle path */
4612  }
4613 
4615  {
4617  DesiredAccess,
4619  MaximumSize,
4622  FileObject);
4623  }
4624 #ifndef NEWCC
4625  else if (FileObject != NULL)
4626  {
4628  DesiredAccess,
4630  MaximumSize,
4633  FileObject,
4634  FileHandle != NULL);
4635  }
4636 #else
4637  else if (FileHandle != NULL || FileObject != NULL)
4638  {
4640  DesiredAccess,
4642  MaximumSize,
4645  FileObject);
4646  }
4647 #endif
4648  else
4649  {
4650  /* All cases should be handled above */
4652  }
4653 
4654  if (FileLock)
4656  if (FileObject)
4658 
4659  return Status;
4660 }
4661 
4662 BOOLEAN
4663 NTAPI
4666  _In_ PVOID Address,
4667  _In_ ULONG Length)
4668 {
4670  BOOLEAN Ret = TRUE;
4672  LARGE_INTEGER SegmentOffset, RangeEnd;
4674 
4676 
4678  if (MemoryArea == NULL)
4679  {
4681  return FALSE;
4682  }
4683 
4684  /* Only supported in old Mm for now */
4686  /* For file mappings */
4688 
4689  Segment = MemoryArea->SectionData.Segment;
4691 
4693  + MemoryArea->SectionData.ViewOffset;
4695  + MemoryArea->SectionData.ViewOffset;
4696 
4697  while (SegmentOffset.QuadPart < RangeEnd.QuadPart)
4698  {
4700  if ((Entry == 0) || IS_SWAP_FROM_SSE(Entry))
4701  {
4702  Ret = FALSE;
4703  break;
4704  }
4705  SegmentOffset.QuadPart += PAGE_SIZE;
4706  }
4707 
4709 
4711  return Ret;
4712 }
4713 
4714 /* Like CcPurgeCache but for the in-memory segment */
4715 BOOLEAN
4716 NTAPI
4720  _In_ ULONG Length)
4721 {
4722  LARGE_INTEGER PurgeStart, PurgeEnd;
4724 
4726  if (!Segment)
4727  {
4728  /* Nothing to purge */
4729  return STATUS_SUCCESS;
4730  }
4731 
4732  PurgeStart.QuadPart = Offset ? Offset->QuadPart : 0LL;
4733  if (Length && Offset)
4734  {
4735  if (!NT_SUCCESS(RtlLongLongAdd(PurgeStart.QuadPart, Length, &PurgeEnd.QuadPart)))
4736  return FALSE;
4737  }
4738 
4740 
4741  if (!Length || !Offset)
4742  {
4743  /* We must calculate the length for ourselves */
4744  /* FIXME: All of this is suboptimal */
4745  ULONG ElemCount = RtlNumberGenericTableElements(&Segment->PageTable);