ReactOS  0.4.15-dev-2359-g0dedb9b
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 #include "ARM3/miarm.h"
54 
55 #undef MmSetPageEntrySectionSegment
56 #define MmSetPageEntrySectionSegment(S,O,E) do { \
57  DPRINT("SetPageEntrySectionSegment(old,%p,%x,%x)\n",(S),(O)->LowPart,E); \
58  _MmSetPageEntrySectionSegment((S),(O),(E),__FILE__,__LINE__); \
59  } while (0)
60 
61 extern MMSESSION MmSession;
62 
63 static LARGE_INTEGER TinyTime = {{-1L, -1L}};
64 
65 #ifndef NEWCC
67 
68 VOID
69 NTAPI
71 {
72  //DPRINT("MmLockSectionSegment(%p,%s:%d)\n", Segment, file, line);
74  Segment->Locked = TRUE;
75 }
76 
77 VOID
78 NTAPI
80 {
81  ASSERT(Segment->Locked);
82  Segment->Locked = FALSE;
84  //DPRINT("MmUnlockSectionSegment(%p,%s:%d)\n", Segment, file, line);
85 }
86 #endif
87 
88 static
91 {
94 
95  while (TRUE)
96  {
97  Segment = SectionObjectPointer->DataSectionObject;
98  if (!Segment)
99  break;
100 
101  if (Segment->SegFlags & (MM_SEGMENT_INCREATE | MM_SEGMENT_INDELETE))
102  {
106  continue;
107  }
108 
109  ASSERT(Segment->SegFlags & MM_DATAFILE_SEGMENT);
110  InterlockedIncrement64(&Segment->RefCount);
111  break;
112  }
113 
115 
116  return Segment;
117 }
118 
119 /* Somewhat grotesque, but eh... */
121 {
122  ASSERT((Segment->SegFlags & MM_DATAFILE_SEGMENT) == 0);
123 
124  return CONTAINING_RECORD(Segment->ReferenceCount, MM_IMAGE_SECTION_OBJECT, RefCount);
125 }
126 
127 NTSTATUS
129  IN PVOID Session,
133 
134 NTSTATUS
135 NTAPI
139  IN PLARGE_INTEGER InputMaximumSize,
144 
145 NTSTATUS
146 NTAPI
156  IN ULONG Protect);
157 
158 //
159 // PeFmtCreateSection depends on the following:
160 //
163 
166 C_ASSERT(FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader) == FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader));
167 
177 
178 /* TYPES *********************************************************************/
179 
180 typedef struct
181 {
189 }
191 
192 /* GLOBALS *******************************************************************/
193 
195 
197 
199 {
200  PAGE_NOACCESS, /* 0 = NONE */
201  PAGE_NOACCESS, /* 1 = SHARED */
202  PAGE_EXECUTE, /* 2 = EXECUTABLE */
203  PAGE_EXECUTE, /* 3 = EXECUTABLE, SHARED */
204  PAGE_READONLY, /* 4 = READABLE */
205  PAGE_READONLY, /* 5 = READABLE, SHARED */
206  PAGE_EXECUTE_READ, /* 6 = READABLE, EXECUTABLE */
207  PAGE_EXECUTE_READ, /* 7 = READABLE, EXECUTABLE, SHARED */
208  /*
209  * FIXME? do we really need the WriteCopy field in segments? can't we use
210  * PAGE_WRITECOPY here?
211  */
212  PAGE_READWRITE, /* 8 = WRITABLE */
213  PAGE_READWRITE, /* 9 = WRITABLE, SHARED */
214  PAGE_EXECUTE_READWRITE, /* 10 = WRITABLE, EXECUTABLE */
215  PAGE_EXECUTE_READWRITE, /* 11 = WRITABLE, EXECUTABLE, SHARED */
216  PAGE_READWRITE, /* 12 = WRITABLE, READABLE */
217  PAGE_READWRITE, /* 13 = WRITABLE, READABLE, SHARED */
218  PAGE_EXECUTE_READWRITE, /* 14 = WRITABLE, READABLE, EXECUTABLE */
219  PAGE_EXECUTE_READWRITE, /* 15 = WRITABLE, READABLE, EXECUTABLE, SHARED */
220 };
221 
222 extern ULONG MmMakeFileAccess [];
225 {
230 };
231 
232 
233 /* FUNCTIONS *****************************************************************/
234 
235 
236 
237 NTSTATUS
238 NTAPI
240  LONGLONG SegOffset,
242 /*
243  * FUNCTION: write a page for a section backed memory area.
244  * PARAMETERS:
245  * MemoryArea - Memory area to write the page for.
246  * Offset - Offset of the page to write.
247  * Page - Page which contains the data to write.
248  */
249 {
252  KEVENT Event;
253  UCHAR MdlBase[sizeof(MDL) + sizeof(PFN_NUMBER)];
254  PMDL Mdl = (PMDL)MdlBase;
255  PFILE_OBJECT FileObject = Segment->FileObject;
257 
258  FileOffset.QuadPart = Segment->Image.FileOffset + SegOffset;
259 
260  RtlZeroMemory(MdlBase, sizeof(MdlBase));
263  Mdl->MdlFlags |= MDL_PAGES_LOCKED;
264 
267  if (Status == STATUS_PENDING)
268  {
270  Status = IoStatus.Status;
271  }
272  if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
273  {
274  MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
275  }
276 
277  return Status;
278 }
279 
280 
281 /*
282  References:
283  [1] Microsoft Corporation, "Microsoft Portable Executable and Common Object
284  File Format Specification", revision 6.0 (February 1999)
285 */
287  IN SIZE_T FileHeaderSize,
288  IN PVOID File,
289  OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
290  OUT PULONG Flags,
291  IN PEXEFMT_CB_READ_FILE ReadFileCb,
292  IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb)
293 {
294  NTSTATUS nStatus;
295  ULONG cbFileHeaderOffsetSize = 0;
296  ULONG cbSectionHeadersOffset = 0;
297  ULONG cbSectionHeadersSize;
298  ULONG cbSectionHeadersOffsetSize = 0;
299  ULONG cbOptHeaderSize;
300  ULONG cbHeadersSize = 0;
301  ULONG nSectionAlignment;
302  ULONG nFileAlignment;
303  ULONG_PTR ImageBase = 0;
304  const IMAGE_DOS_HEADER * pidhDosHeader;
305  const IMAGE_NT_HEADERS32 * pinhNtHeader;
306  const IMAGE_OPTIONAL_HEADER32 * piohOptHeader;
307  const IMAGE_SECTION_HEADER * pishSectionHeaders;
308  PMM_SECTION_SEGMENT pssSegments;
309  LARGE_INTEGER lnOffset;
310  PVOID pBuffer;
311  SIZE_T nPrevVirtualEndOfSegment = 0;
312  ULONG nFileSizeOfHeaders = 0;
313  ULONG i;
314  ULONG AlignedLength;
315 
316  ASSERT(FileHeader);
317  ASSERT(FileHeaderSize > 0);
318  ASSERT(File);
319  ASSERT(ImageSectionObject);
320  ASSERT(ReadFileCb);
321  ASSERT(AllocateSegmentsCb);
322 
323  ASSERT(Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize));
324 
325  ASSERT(((UINT_PTR)FileHeader % TYPE_ALIGNMENT(IMAGE_DOS_HEADER)) == 0);
326 
327 #define DIE(ARGS_) { DPRINT ARGS_; goto l_Return; }
328 
329  pBuffer = NULL;
330  pidhDosHeader = FileHeader;
331 
332  /* DOS HEADER */
334 
335  /* image too small to be an MZ executable */
336  if(FileHeaderSize < sizeof(IMAGE_DOS_HEADER))
337  DIE(("Too small to be an MZ executable, size is %lu\n", FileHeaderSize));
338 
339  /* no MZ signature */
340  if(pidhDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
341  DIE(("No MZ signature found, e_magic is %hX\n", pidhDosHeader->e_magic));
342 
343  /* NT HEADER */
345 
346  /* not a Windows executable */
347  if(pidhDosHeader->e_lfanew <= 0)
348  DIE(("Not a Windows executable, e_lfanew is %d\n", pidhDosHeader->e_lfanew));
349 
350  if(!Intsafe_AddULong32(&cbFileHeaderOffsetSize, pidhDosHeader->e_lfanew, RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader)))
351  DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
352 
353  if(FileHeaderSize < cbFileHeaderOffsetSize)
354  pinhNtHeader = NULL;
355  else
356  {
357  /*
358  * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
359  * and FileHeaderSize >= cbFileHeaderOffsetSize, so this holds true too
360  */
361  ASSERT(Intsafe_CanOffsetPointer(FileHeader, pidhDosHeader->e_lfanew));
362  pinhNtHeader = (PVOID)((UINT_PTR)FileHeader + pidhDosHeader->e_lfanew);
363  }
364 
365  /*
366  * the buffer doesn't contain the NT file header, or the alignment is wrong: we
367  * need to read the header from the file
368  */
369  if(FileHeaderSize < cbFileHeaderOffsetSize ||
370  (UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
371  {
372  ULONG cbNtHeaderSize;
373  ULONG cbReadSize;
374  PVOID pData;
375 
376 l_ReadHeaderFromFile:
377  cbNtHeaderSize = 0;
378  lnOffset.QuadPart = pidhDosHeader->e_lfanew;
379 
380  /* read the header from the file */
381  nStatus = ReadFileCb(File, &lnOffset, sizeof(IMAGE_NT_HEADERS64), &pData, &pBuffer, &cbReadSize);
382 
383  if(!NT_SUCCESS(nStatus))
384  {
385  NTSTATUS ReturnedStatus = nStatus;
386 
387  /* If it attempted to read past the end of the file, it means e_lfanew is invalid */
388  if (ReturnedStatus == STATUS_END_OF_FILE) nStatus = STATUS_INVALID_IMAGE_PROTECT;
389 
390  DIE(("ReadFile failed, status %08X\n", ReturnedStatus));
391  }
392 
393  ASSERT(pData);
394  ASSERT(pBuffer);
395  ASSERT(cbReadSize > 0);
396 
397  nStatus = STATUS_INVALID_IMAGE_FORMAT;
398 
399  /* the buffer doesn't contain the file header */
400  if(cbReadSize < RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader))
401  DIE(("The file doesn't contain the PE file header\n"));
402 
403  pinhNtHeader = pData;
404 
405  /* object still not aligned: copy it to the beginning of the buffer */
406  if((UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
407  {
409  RtlMoveMemory(pBuffer, pData, cbReadSize);
410  pinhNtHeader = pBuffer;
411  }
412 
413  /* invalid NT header */
415 
416  if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
417  DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
418 
419  nStatus = STATUS_INVALID_IMAGE_FORMAT;
420 
421  if(!Intsafe_AddULong32(&cbNtHeaderSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
422  DIE(("The full NT header is too large\n"));
423 
424  /* the buffer doesn't contain the whole NT header */
425  if(cbReadSize < cbNtHeaderSize)
426  DIE(("The file doesn't contain the full NT header\n"));
427  }
428  else
429  {
430  ULONG cbOptHeaderOffsetSize = 0;
431 
433 
434  /* don't trust an invalid NT header */
435  if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
436  DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
437 
438  if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
439  DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
440 
441  nStatus = STATUS_INVALID_IMAGE_FORMAT;
442 
443  if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, cbOptHeaderOffsetSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
444  DIE(("The NT header is too large, SizeOfOptionalHeader is %X\n", pinhNtHeader->FileHeader.SizeOfOptionalHeader));
445 
446  /* the buffer doesn't contain the whole NT header: read it from the file */
447  if(cbOptHeaderOffsetSize > FileHeaderSize)
448  goto l_ReadHeaderFromFile;
449  }
450 
451  /* read information from the NT header */
452  piohOptHeader = &pinhNtHeader->OptionalHeader;
453  cbOptHeaderSize = pinhNtHeader->FileHeader.SizeOfOptionalHeader;
454 
455  nStatus = STATUS_INVALID_IMAGE_FORMAT;
456 
457  if(!RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Magic))
458  DIE(("The optional header doesn't contain the Magic field, SizeOfOptionalHeader is %X\n", cbOptHeaderSize));
459 
460  /* ASSUME: RtlZeroMemory(ImageSectionObject, sizeof(*ImageSectionObject)); */
461 
462  switch(piohOptHeader->Magic)
463  {
465 #ifndef _WIN64
466  nStatus = STATUS_INVALID_IMAGE_WIN_64;
467  DIE(("Win64 optional header, unsupported\n"));
468 #else
469  // Fall through.
470 #endif
472  break;
473  default:
474  DIE(("Unrecognized optional header, Magic is %X\n", piohOptHeader->Magic));
475  }
476 
477  if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SectionAlignment) &&
478  RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, FileAlignment))
479  {
480  /* See [1], section 3.4.2 */
481  if(piohOptHeader->SectionAlignment < PAGE_SIZE)
482  {
483  if(piohOptHeader->FileAlignment != piohOptHeader->SectionAlignment)
484  DIE(("Sections aren't page-aligned and the file alignment isn't the same\n"));
485  }
486  else if(piohOptHeader->SectionAlignment < piohOptHeader->FileAlignment)
487  DIE(("The section alignment is smaller than the file alignment\n"));
488 
489  nSectionAlignment = piohOptHeader->SectionAlignment;
490  nFileAlignment = piohOptHeader->FileAlignment;
491 
492  if(!IsPowerOf2(nSectionAlignment) || !IsPowerOf2(nFileAlignment))
493  DIE(("The section alignment (%u) and file alignment (%u) aren't both powers of 2\n", nSectionAlignment, nFileAlignment));
494  }
495  else
496  {
497  nSectionAlignment = PAGE_SIZE;
498  nFileAlignment = PAGE_SIZE;
499  }
500 
501  ASSERT(IsPowerOf2(nSectionAlignment));
502  ASSERT(IsPowerOf2(nFileAlignment));
503 
504  switch(piohOptHeader->Magic)
505  {
506  /* PE32 */
508  {
509  if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, ImageBase))
510  ImageBase = piohOptHeader->ImageBase;
511 
512  if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfImage))
513  ImageSectionObject->ImageInformation.ImageFileSize = piohOptHeader->SizeOfImage;
514 
515  if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackReserve))
516  ImageSectionObject->ImageInformation.MaximumStackSize = piohOptHeader->SizeOfStackReserve;
517 
518  if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackCommit))
519  ImageSectionObject->ImageInformation.CommittedStackSize = piohOptHeader->SizeOfStackCommit;
520 
521  if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Subsystem))
522  {
523  ImageSectionObject->ImageInformation.SubSystemType = piohOptHeader->Subsystem;
524 
525  if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MinorSubsystemVersion) &&
526  RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MajorSubsystemVersion))
527  {
528  ImageSectionObject->ImageInformation.SubSystemMinorVersion = piohOptHeader->MinorSubsystemVersion;
529  ImageSectionObject->ImageInformation.SubSystemMajorVersion = piohOptHeader->MajorSubsystemVersion;
530  }
531  }
532 
533  if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, AddressOfEntryPoint))
534  {
535  ImageSectionObject->ImageInformation.TransferAddress = (PVOID) (ImageBase +
536  piohOptHeader->AddressOfEntryPoint);
537  }
538 
539  if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfCode))
540  ImageSectionObject->ImageInformation.ImageContainsCode = piohOptHeader->SizeOfCode != 0;
541  else
542  ImageSectionObject->ImageInformation.ImageContainsCode = TRUE;
543 
544  if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, AddressOfEntryPoint))
545  {
546  if (piohOptHeader->AddressOfEntryPoint == 0)
547  {
548  ImageSectionObject->ImageInformation.ImageContainsCode = FALSE;
549  }
550  }
551 
552  if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, LoaderFlags))
553  ImageSectionObject->ImageInformation.LoaderFlags = piohOptHeader->LoaderFlags;
554 
555  if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, DllCharacteristics))
556  {
557  ImageSectionObject->ImageInformation.DllCharacteristics = piohOptHeader->DllCharacteristics;
558 
559  /*
560  * Since we don't really implement SxS yet and LD doesn't supoprt /ALLOWISOLATION:NO, hard-code
561  * this flag here, which will prevent the loader and other code from doing any .manifest or SxS
562  * magic to any binary.
563  *
564  * This will break applications that depend on SxS when running with real Windows Kernel32/SxS/etc
565  * but honestly that's not tested. It will also break them when running no ReactOS once we implement
566  * the SxS support -- at which point, duh, this should be removed.
567  *
568  * But right now, any app depending on SxS is already broken anyway, so this flag only helps.
569  */
570  ImageSectionObject->ImageInformation.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_NO_ISOLATION;
571  }
572 
573  break;
574  }
575 #ifdef _WIN64
576  /* PE64 */
578  {
579  const IMAGE_OPTIONAL_HEADER64 * pioh64OptHeader;
580 
581  pioh64OptHeader = (const IMAGE_OPTIONAL_HEADER64 *)piohOptHeader;
582 
583  if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, ImageBase))
584  {
585  ImageBase = pioh64OptHeader->ImageBase;
586  if(pioh64OptHeader->ImageBase > MAXULONG_PTR)
587  DIE(("ImageBase exceeds the address space\n"));
588  }
589 
590  if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfImage))
591  {
592  if(pioh64OptHeader->SizeOfImage > MAXULONG_PTR)
593  DIE(("SizeOfImage exceeds the address space\n"));
594 
595  ImageSectionObject->ImageInformation.ImageFileSize = pioh64OptHeader->SizeOfImage;
596  }
597 
598  if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackReserve))
599  {
600  if(pioh64OptHeader->SizeOfStackReserve > MAXULONG_PTR)
601  DIE(("SizeOfStackReserve exceeds the address space\n"));
602 
603  ImageSectionObject->ImageInformation.MaximumStackSize = (ULONG_PTR) pioh64OptHeader->SizeOfStackReserve;
604  }
605 
606  if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackCommit))
607  {
608  if(pioh64OptHeader->SizeOfStackCommit > MAXULONG_PTR)
609  DIE(("SizeOfStackCommit exceeds the address space\n"));
610 
611  ImageSectionObject->ImageInformation.CommittedStackSize = (ULONG_PTR) pioh64OptHeader->SizeOfStackCommit;
612  }
613 
614  if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, Subsystem))
615  {
616  ImageSectionObject->ImageInformation.SubSystemType = pioh64OptHeader->Subsystem;
617 
618  if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, MinorSubsystemVersion) &&
619  RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, MajorSubsystemVersion))
620  {
621  ImageSectionObject->ImageInformation.SubSystemMinorVersion = pioh64OptHeader->MinorSubsystemVersion;
622  ImageSectionObject->ImageInformation.SubSystemMajorVersion = pioh64OptHeader->MajorSubsystemVersion;
623  }
624  }
625 
626  if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, AddressOfEntryPoint))
627  {
628  ImageSectionObject->ImageInformation.TransferAddress = (PVOID) (ImageBase +
629  pioh64OptHeader->AddressOfEntryPoint);
630  }
631 
632  if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfCode))
633  ImageSectionObject->ImageInformation.ImageContainsCode = pioh64OptHeader->SizeOfCode != 0;
634  else
635  ImageSectionObject->ImageInformation.ImageContainsCode = TRUE;
636 
637  if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, AddressOfEntryPoint))
638  {
639  if (pioh64OptHeader->AddressOfEntryPoint == 0)
640  {
641  ImageSectionObject->ImageInformation.ImageContainsCode = FALSE;
642  }
643  }
644 
645  if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, LoaderFlags))
646  ImageSectionObject->ImageInformation.LoaderFlags = pioh64OptHeader->LoaderFlags;
647 
648  if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, DllCharacteristics))
649  ImageSectionObject->ImageInformation.DllCharacteristics = pioh64OptHeader->DllCharacteristics;
650 
651  break;
652  }
653 #endif // _WIN64
654  }
655 
656  /* [1], section 3.4.2 */
657  if((ULONG_PTR)ImageBase % 0x10000)
658  DIE(("ImageBase is not aligned on a 64KB boundary"));
659 
660  ImageSectionObject->ImageInformation.ImageCharacteristics = pinhNtHeader->FileHeader.Characteristics;
661  ImageSectionObject->ImageInformation.Machine = pinhNtHeader->FileHeader.Machine;
662  ImageSectionObject->ImageInformation.GpValue = 0;
663  ImageSectionObject->ImageInformation.ZeroBits = 0;
664  ImageSectionObject->BasedAddress = (PVOID)ImageBase;
665 
666  /* SECTION HEADERS */
667  nStatus = STATUS_INVALID_IMAGE_FORMAT;
668 
669  /* see [1], section 3.3 */
670  if(pinhNtHeader->FileHeader.NumberOfSections > 96)
671  DIE(("Too many sections, NumberOfSections is %u\n", pinhNtHeader->FileHeader.NumberOfSections));
672 
673  /*
674  * the additional segment is for the file's headers. They need to be present for
675  * the benefit of the dynamic loader (to locate exports, defaults for thread
676  * parameters, resources, etc.)
677  */
678  ImageSectionObject->NrSegments = pinhNtHeader->FileHeader.NumberOfSections + 1;
679 
680  /* file offset for the section headers */
681  if(!Intsafe_AddULong32(&cbSectionHeadersOffset, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
682  DIE(("Offset overflow\n"));
683 
684  if(!Intsafe_AddULong32(&cbSectionHeadersOffset, cbSectionHeadersOffset, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
685  DIE(("Offset overflow\n"));
686 
687  /* size of the section headers */
689  cbSectionHeadersSize = pinhNtHeader->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
690 
691  if(!Intsafe_AddULong32(&cbSectionHeadersOffsetSize, cbSectionHeadersOffset, cbSectionHeadersSize))
692  DIE(("Section headers too large\n"));
693 
694  /* size of the executable's headers */
695  if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfHeaders))
696  {
697 // if(!IsAligned(piohOptHeader->SizeOfHeaders, nFileAlignment))
698 // DIE(("SizeOfHeaders is not aligned\n"));
699 
700  if(cbSectionHeadersSize > piohOptHeader->SizeOfHeaders)
701  DIE(("The section headers overflow SizeOfHeaders\n"));
702 
703  cbHeadersSize = piohOptHeader->SizeOfHeaders;
704  }
705  else if(!AlignUp(&cbHeadersSize, cbSectionHeadersOffsetSize, nFileAlignment))
706  DIE(("Overflow aligning the size of headers\n"));
707 
708  if(pBuffer)
709  {
711  pBuffer = NULL;
712  }
713  /* WARNING: pinhNtHeader IS NO LONGER USABLE */
714  /* WARNING: piohOptHeader IS NO LONGER USABLE */
715  /* WARNING: pioh64OptHeader IS NO LONGER USABLE */
716 
717  if(FileHeaderSize < cbSectionHeadersOffsetSize)
718  pishSectionHeaders = NULL;
719  else
720  {
721  /*
722  * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
723  * and FileHeaderSize >= cbSectionHeadersOffsetSize, so this holds true too
724  */
725  ASSERT(Intsafe_CanOffsetPointer(FileHeader, cbSectionHeadersOffset));
726  pishSectionHeaders = (PVOID)((UINT_PTR)FileHeader + cbSectionHeadersOffset);
727  }
728 
729  /*
730  * the buffer doesn't contain the section headers, or the alignment is wrong:
731  * read the headers from the file
732  */
733  if(FileHeaderSize < cbSectionHeadersOffsetSize ||
734  (UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0)
735  {
736  PVOID pData;
737  ULONG cbReadSize;
738 
739  lnOffset.QuadPart = cbSectionHeadersOffset;
740 
741  /* read the header from the file */
742  nStatus = ReadFileCb(File, &lnOffset, cbSectionHeadersSize, &pData, &pBuffer, &cbReadSize);
743 
744  if(!NT_SUCCESS(nStatus))
745  DIE(("ReadFile failed with status %08X\n", nStatus));
746 
747  ASSERT(pData);
748  ASSERT(pBuffer);
749  ASSERT(cbReadSize > 0);
750 
751  nStatus = STATUS_INVALID_IMAGE_FORMAT;
752 
753  /* the buffer doesn't contain all the section headers */
754  if(cbReadSize < cbSectionHeadersSize)
755  DIE(("The file doesn't contain all of the section headers\n"));
756 
757  pishSectionHeaders = pData;
758 
759  /* object still not aligned: copy it to the beginning of the buffer */
760  if((UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0)
761  {
763  RtlMoveMemory(pBuffer, pData, cbReadSize);
764  pishSectionHeaders = pBuffer;
765  }
766  }
767 
768  /* SEGMENTS */
769  /* allocate the segments */
771  ImageSectionObject->Segments = AllocateSegmentsCb(ImageSectionObject->NrSegments);
772 
773  if(ImageSectionObject->Segments == NULL)
774  DIE(("AllocateSegments failed\n"));
775 
776  /* initialize the headers segment */
777  pssSegments = ImageSectionObject->Segments;
778 
779 // ASSERT(IsAligned(cbHeadersSize, nFileAlignment));
780 
781  if(!AlignUp(&nFileSizeOfHeaders, cbHeadersSize, nFileAlignment))
782  DIE(("Cannot align the size of the section headers\n"));
783 
784  nPrevVirtualEndOfSegment = ALIGN_UP_BY(cbHeadersSize, nSectionAlignment);
785  if (nPrevVirtualEndOfSegment < cbHeadersSize)
786  DIE(("Cannot align the size of the section headers\n"));
787 
788  pssSegments[0].Image.FileOffset = 0;
789  pssSegments[0].Protection = PAGE_READONLY;
790  pssSegments[0].Length.QuadPart = nPrevVirtualEndOfSegment;
791  pssSegments[0].RawLength.QuadPart = nFileSizeOfHeaders;
792  pssSegments[0].Image.VirtualAddress = 0;
793  pssSegments[0].Image.Characteristics = 0;
794  pssSegments[0].WriteCopy = TRUE;
795 
796  /* skip the headers segment */
797  ++ pssSegments;
798 
799  nStatus = STATUS_INVALID_IMAGE_FORMAT;
800 
801  ASSERT(ImageSectionObject->RefCount > 0);
802 
803  /* convert the executable sections into segments. See also [1], section 4 */
804  for(i = 0; i < ImageSectionObject->NrSegments - 1; ++ i)
805  {
806  ULONG nCharacteristics;
807 
808  /* validate the alignment */
809  if(!IsAligned(pishSectionHeaders[i].VirtualAddress, nSectionAlignment))
810  DIE(("Image.VirtualAddress[%u] is not aligned\n", i));
811 
812  /* sections must be contiguous, ordered by base address and non-overlapping */
813  if(pishSectionHeaders[i].VirtualAddress != nPrevVirtualEndOfSegment)
814  DIE(("Memory gap between section %u and the previous\n", i));
815 
816  /* ignore explicit BSS sections */
817  if(pishSectionHeaders[i].SizeOfRawData != 0)
818  {
819  /* validate the alignment */
820 #if 0
821  /* Yes, this should be a multiple of FileAlignment, but there's
822  * stuff out there that isn't. We can cope with that
823  */
824  if(!IsAligned(pishSectionHeaders[i].SizeOfRawData, nFileAlignment))
825  DIE(("SizeOfRawData[%u] is not aligned\n", i));
826 #endif
827 
828 // if(!IsAligned(pishSectionHeaders[i].PointerToRawData, nFileAlignment))
829 // DIE(("PointerToRawData[%u] is not aligned\n", i));
830 
831  if(!Intsafe_CanAddULong32(pishSectionHeaders[i].PointerToRawData, pishSectionHeaders[i].SizeOfRawData))
832  DIE(("SizeOfRawData[%u] too large\n", i));
833 
834  /* conversion */
835  pssSegments[i].Image.FileOffset = pishSectionHeaders[i].PointerToRawData;
836  pssSegments[i].RawLength.QuadPart = pishSectionHeaders[i].SizeOfRawData;
837  }
838  else
839  {
840  /* FIXME: Should reset PointerToRawData to 0 in the image mapping */
841  ASSERT(pssSegments[i].Image.FileOffset == 0);
842  ASSERT(pssSegments[i].RawLength.QuadPart == 0);
843  }
844 
845  ASSERT(Intsafe_CanAddLong64(pssSegments[i].Image.FileOffset, pssSegments[i].RawLength.QuadPart));
846 
847  nCharacteristics = pishSectionHeaders[i].Characteristics;
848 
849  /* no explicit protection */
850  if((nCharacteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)) == 0)
851  {
852  if(nCharacteristics & IMAGE_SCN_CNT_CODE)
853  nCharacteristics |= IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ;
854 
855  if(nCharacteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
856  nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
857 
858  if(nCharacteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
859  nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
860  }
861 
862  /* see table above */
863  pssSegments[i].Protection = SectionCharacteristicsToProtect[nCharacteristics >> 28];
864  pssSegments[i].WriteCopy = !(nCharacteristics & IMAGE_SCN_MEM_SHARED);
865 
866  if(pishSectionHeaders[i].Misc.VirtualSize == 0)
867  pssSegments[i].Length.QuadPart = pishSectionHeaders[i].SizeOfRawData;
868  else
869  pssSegments[i].Length.QuadPart = pishSectionHeaders[i].Misc.VirtualSize;
870 
871  AlignedLength = ALIGN_UP_BY(pssSegments[i].Length.LowPart, nSectionAlignment);
872  if(AlignedLength < pssSegments[i].Length.LowPart)
873  DIE(("Cannot align the virtual size of section %u\n", i));
874 
875  pssSegments[i].Length.LowPart = AlignedLength;
876 
877  if(pssSegments[i].Length.QuadPart == 0)
878  DIE(("Virtual size of section %u is null\n", i));
879 
880  pssSegments[i].Image.VirtualAddress = pishSectionHeaders[i].VirtualAddress;
881  pssSegments[i].Image.Characteristics = pishSectionHeaders[i].Characteristics;
882 
883  /* ensure the memory image is no larger than 4GB */
884  nPrevVirtualEndOfSegment = (ULONG_PTR)(pssSegments[i].Image.VirtualAddress + pssSegments[i].Length.QuadPart);
885  if (nPrevVirtualEndOfSegment < pssSegments[i].Image.VirtualAddress)
886  DIE(("The image is too large\n"));
887  }
888 
889  if(nSectionAlignment >= PAGE_SIZE)
891 
892  /* Success */
893  nStatus = STATUS_SUCCESS;// STATUS_ROS_EXEFMT_LOADED_FORMAT | EXEFMT_LOADED_PE32;
894 
895 l_Return:
896  if(pBuffer)
898 
899  return nStatus;
900 }
901 
902 /*
903  * FUNCTION: Waits in kernel mode indefinitely for a file object lock.
904  * ARGUMENTS: PFILE_OBJECT to wait for.
905  * RETURNS: Status of the wait.
906  */
907 NTSTATUS
909 {
910  return STATUS_SUCCESS;
911  //return KeWaitForSingleObject(&File->Lock, 0, KernelMode, FALSE, NULL);
912 }
913 
914 
915 
916 VOID
917 NTAPI
919 {
920  ULONG Length;
923  SWAPENTRY SavedSwapEntry;
925 
926  Page = 0;
927 
929 
930  Length = PAGE_ROUND_UP(Segment->Length.QuadPart);
931  for (Offset.QuadPart = 0; Offset.QuadPart < Length; Offset.QuadPart += PAGE_SIZE)
932  {
934  if (Entry)
935  {
937  if (IS_SWAP_FROM_SSE(Entry))
938  {
940  }
941  else
942  {
944  SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
945  if (SavedSwapEntry != 0)
946  {
948  MmFreeSwapPage(SavedSwapEntry);
949  }
951  }
952  }
953  }
954 
956 }
957 
958 static
959 VOID
960 NTAPI
962 {
965 
967 
969 
971 
972  /* This must be either a valid entry or nothing */
974 
975  /* There should be no reference anymore */
977 
979  /* If there is a page, this must be because it's still dirty */
980  ASSERT(Page != 0);
981 
982  /* Write the page */
983  if (IS_DIRTY_SSE(Entry))
984  MiWritePage(Segment, Offset->QuadPart, Page);
985 
987 }
988 
989 VOID
990 NTAPI
992 {
993  KIRQL OldIrql;
994 
995  /* Lock the PFN lock because we mess around with SectionObjectPointers */
997 
998  if (InterlockedDecrement64(Segment->ReferenceCount) > 0)
999  {
1000  /* Nothing to do yet */
1002  return;
1003  }
1004 
1005  *Segment->Flags |= MM_SEGMENT_INDELETE;
1006 
1008 
1009  /* Flush the segment */
1010  if (*Segment->Flags & MM_DATAFILE_SEGMENT)
1011  {
1012  /* Free the page table. This will flush any remaining dirty data */
1014 
1016  /* Delete the pointer on the file */
1017  ASSERT(Segment->FileObject->SectionObjectPointer->DataSectionObject == Segment);
1018  Segment->FileObject->SectionObjectPointer->DataSectionObject = NULL;
1020  ObDereferenceObject(Segment->FileObject);
1021 
1023  }
1024  else
1025  {
1026  /* Most grotesque thing ever */
1027  PMM_IMAGE_SECTION_OBJECT ImageSectionObject = CONTAINING_RECORD(Segment->ReferenceCount, MM_IMAGE_SECTION_OBJECT, RefCount);
1028  PMM_SECTION_SEGMENT SectionSegments;
1029  ULONG NrSegments;
1030  ULONG i;
1031 
1033  /* Delete the pointer on the file */
1034  ASSERT(ImageSectionObject->FileObject->SectionObjectPointer->ImageSectionObject == ImageSectionObject);
1035  ImageSectionObject->FileObject->SectionObjectPointer->ImageSectionObject = NULL;
1037 
1038  ObDereferenceObject(ImageSectionObject->FileObject);
1039 
1040  NrSegments = ImageSectionObject->NrSegments;
1041  SectionSegments = ImageSectionObject->Segments;
1042  for (i = 0; i < NrSegments; i++)
1043  {
1044  if (SectionSegments[i].Image.Characteristics & IMAGE_SCN_MEM_SHARED)
1045  {
1046  MmpFreePageFileSegment(&SectionSegments[i]);
1047  }
1048 
1049  MmFreePageTablesSectionSegment(&SectionSegments[i], NULL);
1050  }
1051 
1052  ExFreePoolWithTag(ImageSectionObject->Segments, TAG_MM_SECTION_SEGMENT);
1053  ExFreePoolWithTag(ImageSectionObject, TAG_MM_SECTION_SEGMENT);
1054  }
1055 }
1056 
1057 VOID
1058 NTAPI
1061 {
1062  ULONG_PTR Entry;
1063 
1065  if (Entry == 0)
1066  {
1067  DPRINT1("Entry == 0 for MmSharePageEntrySectionSegment\n");
1068  KeBugCheck(MEMORY_MANAGEMENT);
1069  }
1071  {
1072  DPRINT1("Maximum share count reached\n");
1073  KeBugCheck(MEMORY_MANAGEMENT);
1074  }
1075  if (IS_SWAP_FROM_SSE(Entry))
1076  {
1077  KeBugCheck(MEMORY_MANAGEMENT);
1078  }
1080 }
1081 
1082 BOOLEAN
1083 NTAPI
1087  BOOLEAN Dirty,
1088  BOOLEAN PageOut,
1089  ULONG_PTR *InEntry)
1090 {
1091  ULONG_PTR Entry = InEntry ? *InEntry : MmGetPageEntrySectionSegment(Segment, Offset);
1093  BOOLEAN IsDataMap = BooleanFlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT);
1094 
1095  if (Entry == 0)
1096  {
1097  DPRINT1("Entry == 0 for MmUnsharePageEntrySectionSegment\n");
1098  KeBugCheck(MEMORY_MANAGEMENT);
1099  }
1100  if (SHARE_COUNT_FROM_SSE(Entry) == 0)
1101  {
1102  DPRINT1("Zero share count for unshare (Seg %p Offset %x Page %x)\n", Segment, Offset->LowPart, PFN_FROM_SSE(Entry));
1103  KeBugCheck(MEMORY_MANAGEMENT);
1104  }
1105  if (IS_SWAP_FROM_SSE(Entry))
1106  {
1107  KeBugCheck(MEMORY_MANAGEMENT);
1108  }
1109  Entry = DECREF_SSE(Entry);
1110  if (Dirty) Entry = DIRTY_SSE(Entry);
1111 
1112  /* If we are paging-out, pruning the page for real will be taken care of in MmCheckDirtySegment */
1113  if ((SHARE_COUNT_FROM_SSE(Entry) > 0) || PageOut)
1114  {
1115  /* Update the page mapping in the segment and we're done */
1117  return FALSE;
1118  }
1119 
1120  /* We are pruning the last mapping on this page. See if we can keep it a bit more. */
1121  ASSERT(!PageOut);
1122 
1123  if (IsDataMap)
1124  {
1125  /* We can always keep memory in for data maps */
1127  return FALSE;
1128  }
1129 
1130  if (!BooleanFlagOn(Segment->Image.Characteristics, IMAGE_SCN_MEM_SHARED))
1131  {
1132  /* So this must have been a read-only page. Keep it ! */
1133  ASSERT(Segment->WriteCopy);
1137  return FALSE;
1138  }
1139 
1140  /*
1141  * So this is a page for a shared section of a DLL.
1142  * We can keep it if it is not dirty.
1143  */
1144  SWAPENTRY SwapEntry = MmGetSavedSwapEntryPage(Page);
1145  if ((SwapEntry == 0) && !IS_DIRTY_SSE(Entry))
1146  {
1148  return FALSE;
1149  }
1150 
1151  /* No more processes are referencing this shared dirty page. Ditch it. */
1152  if (SwapEntry)
1153  {
1155  MmFreeSwapPage(SwapEntry);
1156  }
1159  return TRUE;
1160 }
1161 
1162 static
1163 NTSTATUS
1164 MiCopyFromUserPage(PFN_NUMBER DestPage, const VOID *SrcAddress)
1165 {
1167  KIRQL Irql;
1168  PVOID DestAddress;
1169 
1171  DestAddress = MiMapPageInHyperSpace(Process, DestPage, &Irql);
1172  if (DestAddress == NULL)
1173  {
1174  return STATUS_NO_MEMORY;
1175  }
1176  ASSERT((ULONG_PTR)DestAddress % PAGE_SIZE == 0);
1177  ASSERT((ULONG_PTR)SrcAddress % PAGE_SIZE == 0);
1178  RtlCopyMemory(DestAddress, SrcAddress, PAGE_SIZE);
1179  MiUnmapPageInHyperSpace(Process, DestAddress, Irql);
1180  return STATUS_SUCCESS;
1181 }
1182 
1183 static
1184 NTSTATUS
1185 NTAPI
1189  _In_ ULONG Length,
1190  _In_opt_ PLARGE_INTEGER ValidDataLength)
1191 {
1192  /* Let's use a 64K granularity. */
1193  LONGLONG RangeStart, RangeEnd;
1194  NTSTATUS Status;
1195  PFILE_OBJECT FileObject = Segment->FileObject;
1196 
1197  /* Calculate our range, aligned on 64K if possible. */
1198  Status = RtlLongLongAdd(Offset, Length, &RangeEnd);
1200  if (!NT_SUCCESS(Status))
1201  return Status;
1202 
1203  /* If the file is not random access and we are not the page out thread
1204  * read a 64K Chunk. */
1206  && !FlagOn(FileObject->Flags, FO_RANDOM_ACCESS))
1207  {
1208  RangeStart = Offset - (Offset % _64K);
1209  if (RangeEnd % _64K)
1210  RangeEnd += _64K - (RangeEnd % _64K);
1211  }
1212  else
1213  {
1214  RangeStart = Offset - (Offset % PAGE_SIZE);
1215  if (RangeEnd % PAGE_SIZE)
1216  RangeEnd += PAGE_SIZE - (RangeEnd % PAGE_SIZE);
1217  }
1218 
1219  /* Clamp if needed */
1220  if (!FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT))
1221  {
1222  if (RangeEnd > Segment->RawLength.QuadPart)
1223  RangeEnd = Segment->RawLength.QuadPart;
1224  }
1225 
1226  /* Let's gooooooooo */
1227  for ( ; RangeStart < RangeEnd; RangeStart += _64K)
1228  {
1229  /* First take a look at where we miss pages */
1230  ULONG ToReadPageBits = 0;
1231  LONGLONG ChunkEnd = RangeStart + _64K;
1232 
1233  if (ChunkEnd > RangeEnd)
1234  ChunkEnd = RangeEnd;
1235 
1237  for (LONGLONG ChunkOffset = RangeStart; ChunkOffset < ChunkEnd; ChunkOffset += PAGE_SIZE)
1238  {
1239  LARGE_INTEGER CurrentOffset;
1240 
1241  CurrentOffset.QuadPart = ChunkOffset;
1243 
1244  /* Let any pending read proceed */
1245  while (MM_IS_WAIT_PTE(Entry))
1246  {
1248 
1250 
1252  Entry = MmGetPageEntrySectionSegment(Segment, &CurrentOffset);
1253  }
1254 
1255  if (Entry != 0)
1256  {
1257  /* There is a page here. Or a swap entry. Or whatever... */
1258  continue;
1259  }
1260 
1261  ToReadPageBits |= 1UL << ((ChunkOffset - RangeStart) >> PAGE_SHIFT);
1262 
1263  /* Put a wait entry here */
1264  MmSetPageEntrySectionSegment(Segment, &CurrentOffset, MAKE_SWAP_SSE(MM_WAIT_ENTRY));
1265  }
1267 
1268  if (ToReadPageBits == 0)
1269  {
1270  /* Nothing to do for this chunk */
1271  continue;
1272  }
1273 
1274  /* Now perform the actual read */
1275  LONGLONG ChunkOffset = RangeStart;
1276  while (ChunkOffset < ChunkEnd)
1277  {
1278  /* Move forward if there is a hole */
1279  ULONG BitSet;
1280  if (!_BitScanForward(&BitSet, ToReadPageBits))
1281  {
1282  /* Nothing more to read */
1283  break;
1284  }
1285  ToReadPageBits >>= BitSet;
1286  ChunkOffset += BitSet * PAGE_SIZE;
1287  ASSERT(ChunkOffset < ChunkEnd);
1288 
1289  /* Get the range we have to read */
1290  _BitScanForward(&BitSet, ~ToReadPageBits);
1291  ULONG ReadLength = BitSet * PAGE_SIZE;
1292 
1293  ASSERT(ReadLength <= _64K);
1294 
1295  /* Clamp (This is for image mappings */
1296  if ((ChunkOffset + ReadLength) > ChunkEnd)
1297  ReadLength = ChunkEnd - ChunkOffset;
1298 
1299  ASSERT(ReadLength != 0);
1300 
1301  /* Allocate a MDL */
1303  if (!Mdl)
1304  {
1305  /* Damn. Roll-back. */
1307  while (ChunkOffset < ChunkEnd)
1308  {
1309  if (ToReadPageBits & 1)
1310  {
1311  LARGE_INTEGER CurrentOffset;
1312  CurrentOffset.QuadPart = ChunkOffset;
1314  MmSetPageEntrySectionSegment(Segment, &CurrentOffset, 0);
1315  }
1316  ToReadPageBits >>= 1;
1317  ChunkOffset += PAGE_SIZE;
1318  }
1321  }
1322 
1323  /* Get our pages */
1324  PPFN_NUMBER Pages = MmGetMdlPfnArray(Mdl);
1326  for (UINT i = 0; i < BYTES_TO_PAGES(ReadLength); i++)
1327  {
1328  /* MmRequestPageMemoryConsumer succeeds or bugchecks */
1330  }
1331  Mdl->MdlFlags |= MDL_PAGES_LOCKED | MDL_IO_PAGE_READ;
1332 
1334  FileOffset.QuadPart = Segment->Image.FileOffset + ChunkOffset;
1335 
1336  /* Clamp to VDL */
1337  if (ValidDataLength && ((FileOffset.QuadPart + ReadLength) > ValidDataLength->QuadPart))
1338  {
1339  if (FileOffset.QuadPart > ValidDataLength->QuadPart)
1340  {
1341  /* Great, nothing to read. */
1342  goto AssignPagesToSegment;
1343  }
1344 
1345  Mdl->Size = (FileOffset.QuadPart + ReadLength) - ValidDataLength->QuadPart;
1346  }
1347 
1348  KEVENT Event;
1350 
1351  /* Disable APCs */
1352  KIRQL OldIrql;
1354 
1357  if (Status == STATUS_PENDING)
1358  {
1360  Status = Iosb.Status;
1361  }
1362 
1363  if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
1364  {
1365  MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
1366  }
1367 
1369 
1370  if (Status == STATUS_END_OF_FILE)
1371  {
1372  DPRINT1("Got STATUS_END_OF_FILE at offset %I64d for file %wZ.\n", FileOffset.QuadPart, &FileObject->FileName);
1374  }
1375 
1376  if (!NT_SUCCESS(Status))
1377  {
1378  /* Damn. Roll back. */
1379  for (UINT i = 0; i < BYTES_TO_PAGES(ReadLength); i++)
1381 
1383  while (ChunkOffset < ChunkEnd)
1384  {
1385  if (ToReadPageBits & 1)
1386  {
1387  LARGE_INTEGER CurrentOffset;
1388  CurrentOffset.QuadPart = ChunkOffset;
1390  MmSetPageEntrySectionSegment(Segment, &CurrentOffset, 0);
1391  }
1392  ToReadPageBits >>= 1;
1393  ChunkOffset += PAGE_SIZE;
1394  }
1396  IoFreeMdl(Mdl);;
1397  return Status;
1398  }
1399 
1400 AssignPagesToSegment:
1402 
1403  for (UINT i = 0; i < BYTES_TO_PAGES(ReadLength); i++)
1404  {
1405  LARGE_INTEGER CurrentOffset;
1406  CurrentOffset.QuadPart = ChunkOffset + (i * PAGE_SIZE);
1407 
1409 
1410  MmSetPageEntrySectionSegment(Segment, &CurrentOffset, MAKE_SSE(Pages[i] << PAGE_SHIFT, 0));
1411  }
1412 
1414 
1415  IoFreeMdl(Mdl);
1416  ToReadPageBits >>= BitSet;
1417  ChunkOffset += BitSet * PAGE_SIZE;
1418  }
1419  }
1420 
1421  return STATUS_SUCCESS;
1422 }
1423 
1424 static VOID
1428  ULONG OldType,
1429  ULONG OldProtect,
1430  ULONG NewType,
1431  ULONG NewProtect)
1432 {
1435  BOOLEAN DoCOW = FALSE;
1436  ULONG i;
1438 
1440  ASSERT(MemoryArea != NULL);
1441  Segment = MemoryArea->SectionData.Segment;
1443 
1444  if ((Segment->WriteCopy) &&
1446  {
1447  DoCOW = TRUE;
1448  }
1449 
1450  if (OldProtect != NewProtect)
1451  {
1452  for (i = 0; i < PAGE_ROUND_UP(RegionSize) / PAGE_SIZE; i++)
1453  {
1454  SWAPENTRY SwapEntry;
1455  PVOID Address = (char*)BaseAddress + (i * PAGE_SIZE);
1457 
1458  /* Wait for a wait entry to disappear */
1459  do
1460  {
1461  MmGetPageFileMapping(Process, Address, &SwapEntry);
1462  if (SwapEntry != MM_WAIT_ENTRY)
1463  break;
1466  YieldProcessor();
1469  }
1470  while (TRUE);
1471 
1472  /*
1473  * If we doing COW for this segment then check if the page is
1474  * already private.
1475  */
1476  if (DoCOW && MmIsPagePresent(Process, Address))
1477  {
1479  ULONG_PTR Entry;
1480  PFN_NUMBER Page;
1481 
1483  + MemoryArea->SectionData.ViewOffset;
1485  /*
1486  * An MM_WAIT_ENTRY is ok in this case... It'll just count as
1487  * IS_SWAP_FROM_SSE and we'll do the right thing.
1488  */
1490 
1493  {
1494  Protect = NewProtect;
1495  }
1496  }
1497 
1499  {
1501  Protect);
1502  }
1503  }
1504  }
1505 
1507 }
1508 
1509 NTSTATUS
1510 NTAPI
1513  PVOID Address,
1514  BOOLEAN Locked)
1515 {
1517  PFN_NUMBER Page;
1518  NTSTATUS Status;
1520  ULONG_PTR Entry;
1521  ULONG_PTR Entry1;
1522  ULONG Attributes;
1524  BOOLEAN HasSwapEntry;
1525  PVOID PAddress;
1527  SWAPENTRY SwapEntry;
1528 
1529  ASSERT(Locked);
1530 
1531  /*
1532  * There is a window between taking the page fault and locking the
1533  * address space when another thread could load the page so we check
1534  * that.
1535  */
1537  {
1538  return STATUS_SUCCESS;
1539  }
1540 
1542  {
1543  return STATUS_ACCESS_VIOLATION;
1544  }
1545 
1546  /*
1547  * Check for the virtual memory area being deleted.
1548  */
1550  {
1551  return STATUS_UNSUCCESSFUL;
1552  }
1553 
1554  PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1555  Offset.QuadPart = (ULONG_PTR)PAddress - MA_GetStartingAddress(MemoryArea)
1556  + MemoryArea->SectionData.ViewOffset;
1557 
1558  Segment = MemoryArea->SectionData.Segment;
1560  &MemoryArea->SectionData.RegionListHead,
1561  Address, NULL);
1562  ASSERT(Region != NULL);
1563 
1564  /* Check for a NOACCESS mapping */
1565  if (Region->Protect & PAGE_NOACCESS)
1566  {
1567  return STATUS_ACCESS_VIOLATION;
1568  }
1569 
1570  if (Region->Protect & PAGE_GUARD)
1571  {
1572  /* Remove it */
1574  &MemoryArea->SectionData.RegionListHead,
1575  Address, PAGE_SIZE, Region->Type, Region->Protect & ~PAGE_GUARD,
1577 
1578  if (!NT_SUCCESS(Status))
1579  {
1580  DPRINT1("Removing PAGE_GUARD protection failed : 0x%08x.\n", Status);
1581  }
1582 
1584  }
1585 
1586  HasSwapEntry = MmIsPageSwapEntry(Process, Address);
1587 
1588  /* See if we should use a private page */
1589  if (HasSwapEntry)
1590  {
1591  SWAPENTRY DummyEntry;
1592 
1593  MmGetPageFileMapping(Process, Address, &SwapEntry);
1594  if (SwapEntry == MM_WAIT_ENTRY)
1595  {
1597  YieldProcessor();
1600  }
1601 
1602  /*
1603  * Must be private page we have swapped out.
1604  */
1605 
1606  /*
1607  * Sanity check
1608  */
1609  MmDeletePageFileMapping(Process, Address, &DummyEntry);
1610  ASSERT(DummyEntry == SwapEntry);
1611 
1612  /* Tell everyone else we are serving the fault. */
1613  MmCreatePageFileMapping(Process, Address, MM_WAIT_ENTRY);
1614 
1617  if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1618  if (!Process) MI_SET_PROCESS2("Kernel Section");
1620  if (!NT_SUCCESS(Status))
1621  {
1622  KeBugCheck(MEMORY_MANAGEMENT);
1623  }
1624 
1625  Status = MmReadFromSwapPage(SwapEntry, Page);
1626  if (!NT_SUCCESS(Status))
1627  {
1628  DPRINT1("MmReadFromSwapPage failed, status = %x\n", Status);
1629  KeBugCheck(MEMORY_MANAGEMENT);
1630  }
1631 
1633  MmDeletePageFileMapping(Process, PAddress, &DummyEntry);
1634  ASSERT(DummyEntry == MM_WAIT_ENTRY);
1635 
1637  PAddress,
1638  Region->Protect,
1639  Page);
1640  if (!NT_SUCCESS(Status))
1641  {
1642  DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
1643  KeBugCheck(MEMORY_MANAGEMENT);
1644  return Status;
1645  }
1646 
1647  /*
1648  * Store the swap entry for later use.
1649  */
1650  MmSetSavedSwapEntryPage(Page, SwapEntry);
1651 
1652  /*
1653  * Add the page to the process's working set
1654  */
1656  /*
1657  * Finish the operation
1658  */
1659  DPRINT("Address 0x%p\n", Address);
1660  return STATUS_SUCCESS;
1661  }
1662 
1663  /*
1664  * Lock the segment
1665  */
1667 
1668  /*
1669  * Satisfying a page fault on a map of /Device/PhysicalMemory is easy
1670  */
1671  if ((*Segment->Flags) & MM_PHYSICALMEMORY_SEGMENT)
1672  {
1674  /*
1675  * Just map the desired physical page
1676  */
1677  Page = (PFN_NUMBER)(Offset.QuadPart >> PAGE_SHIFT);
1679  PAddress,
1680  Region->Protect,
1681  Page);
1682  if (!NT_SUCCESS(Status))
1683  {
1684  DPRINT("MmCreateVirtualMappingUnsafe failed, not out of memory\n");
1685  KeBugCheck(MEMORY_MANAGEMENT);
1686  return Status;
1687  }
1688 
1689  /*
1690  * Cleanup and release locks
1691  */
1692  DPRINT("Address 0x%p\n", Address);
1693  return STATUS_SUCCESS;
1694  }
1695 
1696  /*
1697  * Check if this page needs to be mapped COW
1698  */
1699  if ((Segment->WriteCopy) &&
1700  (Region->Protect == PAGE_READWRITE || Region->Protect == PAGE_EXECUTE_READWRITE))
1701  {
1703  }
1704  else
1705  {
1706  Attributes = Region->Protect;
1707  }
1708 
1709 
1710  /*
1711  * Get the entry corresponding to the offset within the section
1712  */
1714  if (Entry == 0)
1715  {
1716  /*
1717  * If the entry is zero, then we need to load the page.
1718  */
1719  if ((Offset.QuadPart >= (LONGLONG)PAGE_ROUND_UP(Segment->RawLength.QuadPart)) && (MemoryArea->VadNode.u.VadFlags.VadType == VadImageMap))
1720  {
1721  /* We are beyond the data which is on file. Just get a new page. */
1723  if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1724  if (!Process) MI_SET_PROCESS2("Kernel Section");
1728 
1730  if (!NT_SUCCESS(Status))
1731  {
1732  DPRINT1("Unable to create virtual mapping\n");
1733  KeBugCheck(MEMORY_MANAGEMENT);
1734  }
1735  ASSERT(MmIsPagePresent(Process, PAddress));
1736  if (Process)
1738 
1739  DPRINT("Address 0x%p\n", Address);
1740  return STATUS_SUCCESS;
1741  }
1742 
1745 
1746  /* The data must be paged in. Lock the file, so that the VDL doesn't get updated behind us. */
1747  FsRtlAcquireFileExclusive(Segment->FileObject);
1748 
1749  PFSRTL_COMMON_FCB_HEADER FcbHeader = Segment->FileObject->FsContext;
1750 
1752 
1753  FsRtlReleaseFile(Segment->FileObject);
1754 
1755  /* Lock address space again */
1757  if (!NT_SUCCESS(Status))
1758  {
1759  /* Damn */
1760  DPRINT1("Failed to page data in!\n");
1761  return STATUS_IN_PAGE_ERROR;
1762  }
1763 
1764  /* Everything went fine. Restart the operation */
1766  }
1767  else if (IS_SWAP_FROM_SSE(Entry))
1768  {
1769  SWAPENTRY SwapEntry;
1770 
1771  SwapEntry = SWAPENTRY_FROM_SSE(Entry);
1772 
1773  /* See if a page op is running on this segment. */
1774  if (SwapEntry == MM_WAIT_ENTRY)
1775  {
1778  YieldProcessor();
1781  }
1782 
1783  /*
1784  * Release all our locks and read in the page from disk
1785  */
1788 
1791  if (!NT_SUCCESS(Status))
1792  {
1793  KeBugCheck(MEMORY_MANAGEMENT);
1794  }
1795 
1796  Status = MmReadFromSwapPage(SwapEntry, Page);
1797  if (!NT_SUCCESS(Status))
1798  {
1799  KeBugCheck(MEMORY_MANAGEMENT);
1800  }
1801 
1802  /*
1803  * Relock the address space and segment
1804  */
1807 
1808  /*
1809  * Check the entry. No one should change the status of a page
1810  * that has a pending page-in.
1811  */
1813  if (Entry1 != MAKE_SWAP_SSE(MM_WAIT_ENTRY))
1814  {
1815  DPRINT1("Someone changed ppte entry while we slept (%x vs %x)\n", Entry, Entry1);
1816  KeBugCheck(MEMORY_MANAGEMENT);
1817  }
1818 
1819  /*
1820  * Save the swap entry.
1821  */
1822  MmSetSavedSwapEntryPage(Page, SwapEntry);
1823 
1824  /* Map the page into the process address space */
1826  PAddress,
1827  Attributes,
1828  Page);
1829  if (!NT_SUCCESS(Status))
1830  {
1831  DPRINT1("Unable to create virtual mapping\n");
1832  KeBugCheck(MEMORY_MANAGEMENT);
1833  }
1834  if (Process)
1836 
1837  /*
1838  * Mark the offset within the section as having valid, in-memory
1839  * data
1840  */
1841  Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
1844 
1845  DPRINT("Address 0x%p\n", Address);
1846  return STATUS_SUCCESS;
1847  }
1848  else
1849  {
1850  /* We already have a page on this section offset. Map it into the process address space. */
1851  Page = PFN_FROM_SSE(Entry);
1852 
1854  PAddress,
1855  Attributes,
1856  Page);
1857  if (!NT_SUCCESS(Status))
1858  {
1859  DPRINT1("Unable to create virtual mapping\n");
1860  KeBugCheck(MEMORY_MANAGEMENT);
1861  }
1862 
1863  if (Process)
1865 
1866  /* Take a reference on it */
1869 
1870  DPRINT("Address 0x%p\n", Address);
1871  return STATUS_SUCCESS;
1872  }
1873 }
1874 
1875 NTSTATUS
1876 NTAPI
1879  PVOID Address,
1880  BOOLEAN Locked)
1881 {
1883  PFN_NUMBER OldPage;
1884  PFN_NUMBER NewPage;
1885  PVOID PAddress;
1888  ULONG_PTR Entry;
1890  BOOLEAN Cow = FALSE;
1891  ULONG NewProtect;
1892 
1893  DPRINT("MmAccessFaultSectionView(%p, %p, %p)\n", AddressSpace, MemoryArea, Address);
1894 
1895  /* Get the region for this address */
1897  &MemoryArea->SectionData.RegionListHead,
1898  Address, NULL);
1899  ASSERT(Region != NULL);
1900  if (!(Region->Protect & PAGE_IS_WRITABLE))
1901  return STATUS_ACCESS_VIOLATION;
1902 
1903  /* Make sure we have a page mapping for this address. */
1905  {
1907  if (!NT_SUCCESS(Status))
1908  {
1909  /* This is invalid access ! */
1910  return Status;
1911  }
1912  }
1913 
1914  /*
1915  * Check if the page has already been set readwrite
1916  */
1918  {
1919  DPRINT("Address 0x%p\n", Address);
1920  return STATUS_SUCCESS;
1921  }
1922 
1923  /* Check if we are doing Copy-On-Write */
1924  Segment = MemoryArea->SectionData.Segment;
1925  Cow = Segment->WriteCopy || (Region->Protect & PAGE_IS_WRITECOPY);
1926 
1927  if (!Cow)
1928  {
1929  /* Simply update page protection and we're done */
1930  MmSetPageProtect(Process, Address, Region->Protect);
1931  return STATUS_SUCCESS;
1932  }
1933 
1934  /* Calculate the new protection & check if we should update the region */
1935  NewProtect = Region->Protect;
1937  {
1939  if (Region->Protect & PAGE_IS_EXECUTABLE)
1941  else
1944  &MemoryArea->SectionData.RegionListHead,
1947  }
1948 
1949  /*
1950  * Find the offset of the page
1951  */
1952  PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1953  Offset.QuadPart = (ULONG_PTR)PAddress - MA_GetStartingAddress(MemoryArea)
1954  + MemoryArea->SectionData.ViewOffset;
1955 
1956  /* Get the page mapping this section offset. */
1959 
1960  /* Get the current page mapping for the process */
1961  ASSERT(MmIsPagePresent(Process, PAddress));
1962  OldPage = MmGetPfnForProcess(Process, PAddress);
1963  ASSERT(OldPage != 0);
1964 
1965  if (IS_SWAP_FROM_SSE(Entry) ||
1966  PFN_FROM_SSE(Entry) != OldPage)
1967  {
1969  /* This is a private page. We must only change the page protection. */
1970  MmSetPageProtect(Process, PAddress, NewProtect);
1971  return STATUS_SUCCESS;
1972  }
1973 
1974  /*
1975  * Allocate a page
1976  */
1978  {
1979  KeBugCheck(MEMORY_MANAGEMENT);
1980  }
1981 
1982  /*
1983  * Copy the old page
1984  */
1985  NT_VERIFY(NT_SUCCESS(MiCopyFromUserPage(NewPage, PAddress)));
1986 
1987  /*
1988  * Unshare the old page.
1989  */
1990  DPRINT("Swapping page (Old %x New %x)\n", OldPage, NewPage);
1991  MmDeleteVirtualMapping(Process, PAddress, NULL, NULL);
1992  if (Process)
1993  MmDeleteRmap(OldPage, Process, PAddress);
1996 
1997  /*
1998  * Set the PTE to point to the new page
1999  */
2000  if (!NT_SUCCESS(MmCreateVirtualMapping(Process, PAddress, NewProtect, NewPage)))
2001  {
2002  DPRINT1("MmCreateVirtualMapping failed, unable to create virtual mapping, not out of memory\n");
2003  KeBugCheck(MEMORY_MANAGEMENT);
2004  }
2005 
2006  if (Process)
2007  MmInsertRmap(NewPage, Process, PAddress);
2008 
2009  DPRINT("Address 0x%p\n", Address);
2010  return STATUS_SUCCESS;
2011 }
2012 
2013 NTSTATUS
2014 NTAPI
2018  SIZE_T Length,
2019  ULONG Protect,
2020  PULONG OldProtect)
2021 {
2023  NTSTATUS Status;
2024  ULONG_PTR MaxLength;
2025 
2027  if (Length > MaxLength)
2028  Length = (ULONG)MaxLength;
2029 
2031  &MemoryArea->SectionData.RegionListHead,
2032  BaseAddress, NULL);
2033  ASSERT(Region != NULL);
2034 
2035  if ((MemoryArea->Flags & SEC_NO_CHANGE) &&
2036  Region->Protect != Protect)
2037  {
2039  }
2040 
2041  *OldProtect = Region->Protect;
2043  &MemoryArea->SectionData.RegionListHead,
2044  BaseAddress, Length, Region->Type, Protect,
2046 
2047  return Status;
2048 }
2049 
2052  PVOID Address,
2055 {
2057  PVOID RegionBaseAddress;
2059 
2061  &MemoryArea->SectionData.RegionListHead,
2062  Address, &RegionBaseAddress);
2063  if (Region == NULL)
2064  {
2065  return STATUS_UNSUCCESSFUL;
2066  }
2067 
2069  {
2070  Segment = MemoryArea->SectionData.Segment;
2071  Info->AllocationBase = (PUCHAR)MA_GetStartingAddress(MemoryArea) - Segment->Image.VirtualAddress;
2072  Info->Type = MEM_IMAGE;
2073  }
2074  else
2075  {
2076  Info->AllocationBase = (PVOID)MA_GetStartingAddress(MemoryArea);
2077  Info->Type = MEM_MAPPED;
2078  }
2079  Info->BaseAddress = RegionBaseAddress;
2080  Info->AllocationProtect = MmProtectToValue[MemoryArea->VadNode.u.VadFlags.Protection];
2081  Info->RegionSize = Region->Length;
2082  Info->State = MEM_COMMIT;
2083  Info->Protect = Region->Protect;
2084 
2086  return STATUS_SUCCESS;
2087 }
2088 
2089 VOID NTAPI
2091 {
2092  PSECTION Section = ObjectBody;
2093 
2094  /* Check if it's an ARM3, or ReactOS section */
2095  if (!MiIsRosSectionObject(Section))
2096  {
2097  MiDeleteARM3Section(ObjectBody);
2098  return;
2099  }
2100 
2101  DPRINT("MmpDeleteSection(ObjectBody %p)\n", ObjectBody);
2102  if (Section->u.Flags.Image)
2103  {
2104  PMM_IMAGE_SECTION_OBJECT ImageSectionObject = (PMM_IMAGE_SECTION_OBJECT)Section->Segment;
2105 
2106  /*
2107  * NOTE: Section->ImageSection can be NULL for short time
2108  * during the section creating. If we fail for some reason
2109  * until the image section is properly initialized we shouldn't
2110  * process further here.
2111  */
2112  if (Section->Segment == NULL)
2113  return;
2114 
2115  /* We just dereference the first segment */
2116  ASSERT(ImageSectionObject->RefCount > 0);
2117  MmDereferenceSegment(ImageSectionObject->Segments);
2118  }
2119  else
2120  {
2122 
2123  /*
2124  * NOTE: Section->Segment can be NULL for short time
2125  * during the section creating.
2126  */
2127  if (Segment == NULL)
2128  return;
2129 
2130  Segment->SectionCount--;
2132  }
2133 }
2134 
2135 VOID NTAPI
2137  IN PVOID Object,
2140  IN ULONG SystemHandleCount)
2141 {
2142  DPRINT("MmpCloseSection(OB %p, HC %lu)\n", Object, ProcessHandleCount);
2143 }
2144 
2145 CODE_SEG("INIT")
2146 NTSTATUS
2147 NTAPI
2149 {
2150  PSECTION PhysSection;
2151  NTSTATUS Status;
2153  UNICODE_STRING Name = RTL_CONSTANT_STRING(L"\\Device\\PhysicalMemory");
2154  LARGE_INTEGER SectionSize;
2155  HANDLE Handle;
2157 
2158  /*
2159  * Create the section mapping physical memory
2160  */
2161  SectionSize.QuadPart = MmHighestPhysicalPage * PAGE_SIZE;
2163  &Name,
2165  NULL,
2166  NULL);
2167  /*
2168  * Create the Object
2169  */
2172  &Obj,
2174  NULL,
2175  sizeof(*PhysSection),
2176  0,
2177  0,
2178  (PVOID*)&PhysSection);
2179  if (!NT_SUCCESS(Status))
2180  {
2181  DPRINT1("MmCreatePhysicalMemorySection: failed to create object (0x%lx)\n", Status);
2182  return Status;
2183  }
2184 
2185  /*
2186  * Initialize it
2187  */
2188  RtlZeroMemory(PhysSection, sizeof(*PhysSection));
2189 
2190  /* Mark this as a "ROS Section" */
2191  PhysSection->u.Flags.filler = 1;
2192  PhysSection->InitialPageProtection = PAGE_EXECUTE_READWRITE;
2193  PhysSection->u.Flags.PhysicalMemory = 1;
2194  PhysSection->SizeOfSection = SectionSize;
2197  if (Segment == NULL)
2198  {
2199  ObDereferenceObject(PhysSection);
2200  return STATUS_NO_MEMORY;
2201  }
2203  PhysSection->Segment = (PSEGMENT)Segment;
2204  Segment->RefCount = 1;
2205 
2206  Segment->ReferenceCount = &Segment->RefCount;
2207  Segment->Flags = &Segment->SegFlags;
2208 
2210  Segment->Image.FileOffset = 0;
2211  Segment->Protection = PAGE_EXECUTE_READWRITE;
2212  Segment->RawLength = SectionSize;
2213  Segment->Length = SectionSize;
2214  Segment->SegFlags = MM_PHYSICALMEMORY_SEGMENT;
2215  Segment->WriteCopy = FALSE;
2216  Segment->Image.VirtualAddress = 0;
2217  Segment->Image.Characteristics = 0;
2219 
2220  Status = ObInsertObject(PhysSection,
2221  NULL,
2223  0,
2224  NULL,
2225  &Handle);
2226  if (!NT_SUCCESS(Status))
2227  {
2228  ObDereferenceObject(PhysSection);
2229  return Status;
2230  }
2232 
2233  return STATUS_SUCCESS;
2234 }
2235 
2236 CODE_SEG("INIT")
2237 NTSTATUS
2238 NTAPI
2240 {
2241  OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
2243 
2244  DPRINT("Creating Section Object Type\n");
2245 
2246  /* Initialize the section based root */
2249 
2250  /* Initialize the Section object type */
2251  RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
2252  RtlInitUnicodeString(&Name, L"Section");
2253  ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
2254  ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(SECTION);
2255  ObjectTypeInitializer.PoolType = PagedPool;
2256  ObjectTypeInitializer.UseDefaultObject = TRUE;
2257  ObjectTypeInitializer.GenericMapping = MmpSectionMapping;
2258  ObjectTypeInitializer.DeleteProcedure = MmpDeleteSection;
2259  ObjectTypeInitializer.CloseProcedure = MmpCloseSection;
2260  ObjectTypeInitializer.ValidAccessMask = SECTION_ALL_ACCESS;
2261  ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
2262  ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &MmSectionObjectType);
2263 
2265 
2266  return STATUS_SUCCESS;
2267 }
2268 
2269 static
2270 NTSTATUS
2271 NTAPI
2275  PLARGE_INTEGER UMaximumSize,
2279  BOOLEAN GotFileHandle)
2280 /*
2281  * Create a section backed by a data file
2282  */
2283 {
2284  PSECTION Section;
2285  NTSTATUS Status;
2288  KIRQL OldIrql;
2289 
2290  /*
2291  * Create the section
2292  */
2297  NULL,
2298  sizeof(*Section),
2299  0,
2300  0,
2301  (PVOID*)&Section);
2302  if (!NT_SUCCESS(Status))
2303  {
2304  return Status;
2305  }
2306  /*
2307  * Initialize it
2308  */
2309  RtlZeroMemory(Section, sizeof(*Section));
2310 
2311  /* Mark this as a "ROS" section */
2312  Section->u.Flags.filler = 1;
2313  Section->InitialPageProtection = SectionPageProtection;
2314  Section->u.Flags.File = 1;
2315 
2317  Section->u.Flags.NoChange = 1;
2319  Section->u.Flags.Reserve = 1;
2320 
2321  if (!GotFileHandle)
2322  {
2323  ASSERT(UMaximumSize != NULL);
2324  // ASSERT(UMaximumSize->QuadPart != 0);
2325  MaximumSize = *UMaximumSize;
2326  }
2327  else
2328  {
2331  if (!NT_SUCCESS(Status))
2332  {
2333  ObDereferenceObject(Section);
2334  return Status;
2335  }
2336 
2337  /*
2338  * FIXME: Revise this once a locking order for file size changes is
2339  * decided
2340  */
2341  if ((UMaximumSize != NULL) && (UMaximumSize->QuadPart != 0))
2342  {
2343  MaximumSize = *UMaximumSize;
2344  }
2345  else
2346  {
2348  /* Mapping zero-sized files isn't allowed. */
2349  if (MaximumSize.QuadPart == 0)
2350  {
2351  ObDereferenceObject(Section);
2353  }
2354  }
2355 
2356  if (MaximumSize.QuadPart > FileSize.QuadPart)
2357  {
2360  sizeof(LARGE_INTEGER),
2361  &MaximumSize);
2362  if (!NT_SUCCESS(Status))
2363  {
2364  ObDereferenceObject(Section);
2366  }
2367  }
2368  }
2369 
2370  if (FileObject->SectionObjectPointer == NULL)
2371  {
2372  ObDereferenceObject(Section);
2374  }
2375 
2376  /*
2377  * Lock the file
2378  */
2380  if (Status != STATUS_SUCCESS)
2381  {
2382  ObDereferenceObject(Section);
2383  return Status;
2384  }
2385 
2386  /* Lock the PFN lock while messing with Section Object pointers */
2388  Segment = FileObject->SectionObjectPointer->DataSectionObject;
2389 
2390  while (Segment && (Segment->SegFlags & (MM_SEGMENT_INDELETE | MM_SEGMENT_INCREATE)))
2391  {
2395  Segment = FileObject->SectionObjectPointer->DataSectionObject;
2396  }
2397 
2398  /*
2399  * If this file hasn't been mapped as a data file before then allocate a
2400  * section segment to describe the data file mapping
2401  */
2402  if (Segment == NULL)
2403  {
2406  if (Segment == NULL)
2407  {
2408  //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
2410  ObDereferenceObject(Section);
2411  return STATUS_NO_MEMORY;
2412  }
2413 
2414  /* We are creating it */
2415  RtlZeroMemory(Segment, sizeof(*Segment));
2417  Segment->RefCount = 1;
2418 
2419  FileObject->SectionObjectPointer->DataSectionObject = Segment;
2420 
2421  /* We're safe to release the lock now */
2423 
2424  Section->Segment = (PSEGMENT)Segment;
2425 
2426  /* Self-referencing segment */
2427  Segment->Flags = &Segment->SegFlags;
2428  Segment->ReferenceCount = &Segment->RefCount;
2429 
2430  Segment->SectionCount = 1;
2431 
2433  Segment->FileObject = FileObject;
2435 
2436  Segment->Image.FileOffset = 0;
2437  Segment->Protection = SectionPageProtection;
2438 
2439  Segment->Image.Characteristics = 0;
2442  {
2443  Segment->Length.QuadPart = Segment->RawLength.QuadPart = 0;
2444  }
2445  else
2446  {
2447  Segment->RawLength.QuadPart = MaximumSize.QuadPart;
2448  Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
2449  }
2450  Segment->Image.VirtualAddress = 0;
2452 
2453  /* We're good to use it now */
2455  Segment->SegFlags &= ~MM_SEGMENT_INCREATE;
2457  }
2458  else
2459  {
2460  Section->Segment = (PSEGMENT)Segment;
2461  Segment->RefCount++;
2462  InterlockedIncrementUL(&Segment->SectionCount);
2463 
2465 
2467 
2468  if (MaximumSize.QuadPart > Segment->RawLength.QuadPart &&
2470  {
2471  Segment->RawLength.QuadPart = MaximumSize.QuadPart;
2472  Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
2473  }
2474 
2476  }
2477  Section->SizeOfSection = MaximumSize;
2478 
2479  //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
2480  *SectionObject = Section;
2481  return STATUS_SUCCESS;
2482 }
2483 
2484 /*
2485  TODO: not that great (declaring loaders statically, having to declare all of
2486  them, having to keep them extern, etc.), will fix in the future
2487 */
2489 (
2490  IN CONST VOID * FileHeader,
2491  IN SIZE_T FileHeaderSize,
2492  IN PVOID File,
2493  OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2494  OUT PULONG Flags,
2495  IN PEXEFMT_CB_READ_FILE ReadFileCb,
2496  IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
2497 );
2498 
2500 (
2501  IN CONST VOID * FileHeader,
2502  IN SIZE_T FileHeaderSize,
2503  IN PVOID File,
2504  OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2505  OUT PULONG Flags,
2506  IN PEXEFMT_CB_READ_FILE ReadFileCb,
2507  IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
2508 );
2509 
2511 {
2513 #ifdef __ELF
2515 #endif
2516 };
2517 
2518 static
2520 NTAPI
2522 {
2523  SIZE_T SizeOfSegments;
2524  PMM_SECTION_SEGMENT Segments;
2525 
2526  /* TODO: check for integer overflow */
2527  SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * NrSegments;
2528 
2530  SizeOfSegments,
2532 
2533  if(Segments)
2534  RtlZeroMemory(Segments, SizeOfSegments);
2535 
2536  return Segments;
2537 }
2538 static
2539 NTSTATUS
2540 NTAPI
2543  IN ULONG Length,
2544  OUT PVOID * Data,
2545  OUT PVOID * AllocBase,
2546  OUT PULONG ReadSize)
2547 {
2548  NTSTATUS Status;
2550  ULONG AdjustOffset;
2551  ULONG OffsetAdjustment;
2552  ULONG BufferSize;
2553  ULONG UsedSize;
2554  PVOID Buffer;
2557 
2559 
2560  if(Length == 0)
2561  {
2562  KeBugCheck(MEMORY_MANAGEMENT);
2563  }
2564 
2565  FileOffset = *Offset;
2566 
2567  /* Negative/special offset: it cannot be used in this context */
2568  if(FileOffset.u.HighPart < 0)
2569  {
2570  KeBugCheck(MEMORY_MANAGEMENT);
2571  }
2572 
2573  AdjustOffset = PAGE_ROUND_DOWN(FileOffset.u.LowPart);
2574  OffsetAdjustment = FileOffset.u.LowPart - AdjustOffset;
2575  FileOffset.u.LowPart = AdjustOffset;
2576 
2577  BufferSize = Length + OffsetAdjustment;
2579 
2580  /*
2581  * It's ok to use paged pool, because this is a temporary buffer only used in
2582  * the loading of executables. The assumption is that MmCreateSection is
2583  * always called at low IRQLs and that these buffers don't survive a brief
2584  * initialization phase
2585  */
2587  if (!Buffer)
2588  {
2590  }
2591 
2593 
2594  UsedSize = (ULONG)Iosb.Information;
2595 
2596  if(NT_SUCCESS(Status) && UsedSize < OffsetAdjustment)
2597  {
2600  }
2601 
2602  if(NT_SUCCESS(Status))
2603  {
2604  *Data = (PVOID)((ULONG_PTR)Buffer + OffsetAdjustment);
2605  *AllocBase = Buffer;
2606  *ReadSize = UsedSize - OffsetAdjustment;
2607  }
2608  else
2609  {
2610  ExFreePoolWithTag(Buffer, 'rXmM');
2611  }
2612 
2613  return Status;
2614 }
2615 
2616 #ifdef NASSERT
2617 # define MmspAssertSegmentsSorted(OBJ_) ((void)0)
2618 # define MmspAssertSegmentsNoOverlap(OBJ_) ((void)0)
2619 # define MmspAssertSegmentsPageAligned(OBJ_) ((void)0)
2620 #else
2621 static
2622 VOID
2623 NTAPI
2625 {
2626  ULONG i;
2627 
2628  for( i = 1; i < ImageSectionObject->NrSegments; ++ i )
2629  {
2630  ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >=
2631  ImageSectionObject->Segments[i - 1].Image.VirtualAddress);
2632  }
2633 }
2634 
2635 static
2636 VOID
2637 NTAPI
2639 {
2640  ULONG i;
2641 
2642  MmspAssertSegmentsSorted(ImageSectionObject);
2643 
2644  for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2645  {
2646  ASSERT(ImageSectionObject->Segments[i].Length.QuadPart > 0);
2647 
2648  if(i > 0)
2649  {
2650  ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >=
2651  (ImageSectionObject->Segments[i - 1].Image.VirtualAddress +
2652  ImageSectionObject->Segments[i - 1].Length.QuadPart));
2653  }
2654  }
2655 }
2656 
2657 static
2658 VOID
2659 NTAPI
2661 {
2662  ULONG i;
2663 
2664  for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2665  {
2666  ASSERT((ImageSectionObject->Segments[i].Image.VirtualAddress % PAGE_SIZE) == 0);
2667  ASSERT((ImageSectionObject->Segments[i].Length.QuadPart % PAGE_SIZE) == 0);
2668  }
2669 }
2670 #endif
2671 
2672 static
2673 int
2674 __cdecl
2675 MmspCompareSegments(const void * x,
2676  const void * y)
2677 {
2678  const MM_SECTION_SEGMENT *Segment1 = (const MM_SECTION_SEGMENT *)x;
2679  const MM_SECTION_SEGMENT *Segment2 = (const MM_SECTION_SEGMENT *)y;
2680 
2681  if (Segment1->Image.VirtualAddress > Segment2->Image.VirtualAddress)
2682  return 1;
2683  else if (Segment1->Image.VirtualAddress < Segment2->Image.VirtualAddress)
2684  return -1;
2685  else
2686  return 0;
2687 }
2688 
2689 /*
2690  * Ensures an image section's segments are sorted in memory
2691  */
2692 static
2693 VOID
2694 NTAPI
2696  IN ULONG Flags)
2697 {
2699  {
2700  MmspAssertSegmentsSorted(ImageSectionObject);
2701  }
2702  else
2703  {
2704  qsort(ImageSectionObject->Segments,
2705  ImageSectionObject->NrSegments,
2706  sizeof(ImageSectionObject->Segments[0]),
2708  }
2709 }
2710 
2711 
2712 /*
2713  * Ensures an image section's segments don't overlap in memory and don't have
2714  * gaps and don't have a null size. We let them map to overlapping file regions,
2715  * though - that's not necessarily an error
2716  */
2717 static
2718 BOOLEAN
2719 NTAPI
2722  IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2723  IN ULONG Flags
2724 )
2725 {
2726  ULONG i;
2727 
2729  {
2730  MmspAssertSegmentsNoOverlap(ImageSectionObject);
2731  return TRUE;
2732  }
2733 
2734  ASSERT(ImageSectionObject->NrSegments >= 1);
2735 
2736  for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2737  {
2738  if(ImageSectionObject->Segments[i].Length.QuadPart == 0)
2739  {
2740  return FALSE;
2741  }
2742 
2743  if(i > 0)
2744  {
2745  /*
2746  * TODO: relax the limitation on gaps. For example, gaps smaller than a
2747  * page could be OK (Windows seems to be OK with them), and larger gaps
2748  * could lead to image sections spanning several discontiguous regions
2749  * (NtMapViewOfSection could then refuse to map them, and they could
2750  * e.g. only be allowed as parameters to NtCreateProcess, like on UNIX)
2751  */
2752  if ((ImageSectionObject->Segments[i - 1].Image.VirtualAddress +
2753  ImageSectionObject->Segments[i - 1].Length.QuadPart) !=
2754  ImageSectionObject->Segments[i].Image.VirtualAddress)
2755  {
2756  return FALSE;
2757  }
2758  }
2759  }
2760 
2761  return TRUE;
2762 }
2763 
2764 /*
2765  * Merges and pads an image section's segments until they all are page-aligned
2766  * and have a size that is a multiple of the page size
2767  */
2768 static
2769 BOOLEAN
2770 NTAPI
2773  IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2774  IN ULONG Flags
2775 )
2776 {
2777  ULONG i;
2778  ULONG LastSegment;
2779  PMM_SECTION_SEGMENT EffectiveSegment;
2780 
2782  {
2783  MmspAssertSegmentsPageAligned(ImageSectionObject);
2784  return TRUE;
2785  }
2786 
2787  LastSegment = 0;
2788  EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
2789 
2790  for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2791  {
2792  /*
2793  * The first segment requires special handling
2794  */
2795  if (i == 0)
2796  {
2798  ULONG_PTR VirtualOffset;
2799 
2800  VirtualAddress = EffectiveSegment->Image.VirtualAddress;
2801 
2802  /* Round down the virtual address to the nearest page */
2803  EffectiveSegment->Image.VirtualAddress = PAGE_ROUND_DOWN(VirtualAddress);
2804 
2805  /* Round up the virtual size to the nearest page */
2806  EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(VirtualAddress + EffectiveSegment->Length.QuadPart) -
2807  EffectiveSegment->Image.VirtualAddress;
2808 
2809  /* Adjust the raw address and size */
2810  VirtualOffset = VirtualAddress - EffectiveSegment->Image.VirtualAddress;
2811 
2812  if (EffectiveSegment->Image.FileOffset < VirtualOffset)
2813  {
2814  return FALSE;
2815  }
2816 
2817  /*
2818  * Garbage in, garbage out: unaligned base addresses make the file
2819  * offset point in curious and odd places, but that's what we were
2820  * asked for
2821  */
2822  EffectiveSegment->Image.FileOffset -= VirtualOffset;
2823  EffectiveSegment->RawLength.QuadPart += VirtualOffset;
2824  }
2825  else
2826  {
2827  PMM_SECTION_SEGMENT Segment = &ImageSectionObject->Segments[i];
2828  ULONG_PTR EndOfEffectiveSegment;
2829 
2830  EndOfEffectiveSegment = (ULONG_PTR)(EffectiveSegment->Image.VirtualAddress + EffectiveSegment->Length.QuadPart);
2831  ASSERT((EndOfEffectiveSegment % PAGE_SIZE) == 0);
2832 
2833  /*
2834  * The current segment begins exactly where the current effective
2835  * segment ended, therefore beginning a new effective segment
2836  */
2837  if (EndOfEffectiveSegment == Segment->Image.VirtualAddress)
2838  {
2839  LastSegment ++;
2840  ASSERT(LastSegment <= i);
2841  ASSERT(LastSegment < ImageSectionObject->NrSegments);
2842 
2843  EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
2844 
2845  if (LastSegment != i)
2846  {
2847  /*
2848  * Copy the current segment. If necessary, the effective segment
2849  * will be expanded later
2850  */
2851  *EffectiveSegment = *Segment;
2852  }
2853 
2854  /*
2855  * Page-align the virtual size. We know for sure the virtual address
2856  * already is
2857  */
2858  ASSERT((EffectiveSegment->Image.VirtualAddress % PAGE_SIZE) == 0);
2859  EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(EffectiveSegment->Length.QuadPart);
2860  }
2861  /*
2862  * The current segment is still part of the current effective segment:
2863  * extend the effective segment to reflect this
2864  */
2865  else if (EndOfEffectiveSegment > Segment->Image.VirtualAddress)
2866  {
2867  static const ULONG FlagsToProtection[16] =
2868  {
2869  PAGE_NOACCESS,
2870  PAGE_READONLY,
2885  };
2886 
2887  unsigned ProtectionFlags;
2888 
2889  /*
2890  * Extend the file size
2891  */
2892 
2893  /* Unaligned segments must be contiguous within the file */
2894  if (Segment->Image.FileOffset != (EffectiveSegment->Image.FileOffset +
2895  EffectiveSegment->RawLength.QuadPart))
2896  {
2897  return FALSE;
2898  }
2899 
2900  EffectiveSegment->RawLength.QuadPart += Segment->RawLength.QuadPart;
2901 
2902  /*
2903  * Extend the virtual size
2904  */
2905  ASSERT(PAGE_ROUND_UP(Segment->Image.VirtualAddress + Segment->Length.QuadPart) >= EndOfEffectiveSegment);
2906 
2907  EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(Segment->Image.VirtualAddress + Segment->Length.QuadPart) -
2908  EffectiveSegment->Image.VirtualAddress;
2909 
2910  /*
2911  * Merge the protection
2912  */
2913  EffectiveSegment->Protection |= Segment->Protection;
2914 
2915  /* Clean up redundance */
2916  ProtectionFlags = 0;
2917 
2918  if(EffectiveSegment->Protection & PAGE_IS_READABLE)
2919  ProtectionFlags |= 1 << 0;
2920 
2921  if(EffectiveSegment->Protection & PAGE_IS_WRITABLE)
2922  ProtectionFlags |= 1 << 1;
2923 
2924  if(EffectiveSegment->Protection & PAGE_IS_EXECUTABLE)
2925  ProtectionFlags |= 1 << 2;
2926 
2927  if(EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
2928  ProtectionFlags |= 1 << 3;
2929 
2930  ASSERT(ProtectionFlags < 16);
2931  EffectiveSegment->Protection = FlagsToProtection[ProtectionFlags];
2932 
2933  /* If a segment was required to be shared and cannot, fail */
2934  if(!(Segment->Protection & PAGE_IS_WRITECOPY) &&
2935  EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
2936  {
2937  return FALSE;
2938  }
2939  }
2940  /*
2941  * We assume no holes between segments at this point
2942  */
2943  else
2944  {
2945  KeBugCheck(MEMORY_MANAGEMENT);
2946  }
2947  }
2948  }
2949  ImageSectionObject->NrSegments = LastSegment + 1;
2950 
2951  return TRUE;
2952 }
2953 
2954 NTSTATUS
2956  PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
2957 {
2959  PVOID FileHeader;
2960  PVOID FileHeaderBuffer;
2961  ULONG FileHeaderSize;
2962  ULONG Flags;
2963  ULONG OldNrSegments;
2964  NTSTATUS Status;
2965  ULONG i;
2966 
2967  /*
2968  * Read the beginning of the file (2 pages). Should be enough to contain
2969  * all (or most) of the headers
2970  */
2971  Offset.QuadPart = 0;
2972 
2974  &Offset,
2975  PAGE_SIZE * 2,
2976  &FileHeader,
2977  &FileHeaderBuffer,
2978  &FileHeaderSize);
2979 
2980  if (!NT_SUCCESS(Status))
2981  return Status;
2982 
2983  if (FileHeaderSize == 0)
2984  {
2985  ExFreePool(FileHeaderBuffer);
2986  return STATUS_UNSUCCESSFUL;
2987  }
2988 
2989  /*
2990  * Look for a loader that can handle this executable
2991  */
2992  for (i = 0; i < RTL_NUMBER_OF(ExeFmtpLoaders); ++ i)
2993  {
2994  Flags = 0;
2995 
2996  Status = ExeFmtpLoaders[i](FileHeader,
2997  FileHeaderSize,
2998  FileObject,
2999  ImageSectionObject,
3000  &Flags,
3003 
3004  if (!NT_SUCCESS(Status))
3005  {
3006  if (ImageSectionObject->Segments)
3007  {
3008  ExFreePool(ImageSectionObject->Segments);
3009  ImageSectionObject->Segments = NULL;
3010  }
3011  }
3012 
3014  break;
3015  }
3016 
3017  ExFreePoolWithTag(FileHeaderBuffer, 'rXmM');
3018 
3019  /*
3020  * No loader handled the format
3021  */
3023  {
3026  }
3027 
3028  if (!NT_SUCCESS(Status))
3029  return Status;
3030 
3031  ASSERT(ImageSectionObject->Segments != NULL);
3032  ASSERT(ImageSectionObject->RefCount > 0);
3033 
3034  /*
3035  * Some defaults
3036  */
3037  /* FIXME? are these values platform-dependent? */
3038  if (ImageSectionObject->ImageInformation.MaximumStackSize == 0)
3039  ImageSectionObject->ImageInformation.MaximumStackSize = 0x40000;
3040 
3041  if(ImageSectionObject->ImageInformation.CommittedStackSize == 0)
3042  ImageSectionObject->ImageInformation.CommittedStackSize = 0x1000;
3043 
3044  if(ImageSectionObject->BasedAddress == NULL)
3045  {
3046  if(ImageSectionObject->ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL)
3047  ImageSectionObject->BasedAddress = (PVOID)0x10000000;
3048  else
3049  ImageSectionObject->BasedAddress = (PVOID)0x00400000;
3050  }
3051 
3052  /*
3053  * And now the fun part: fixing the segments
3054  */
3055 
3056  /* Sort them by virtual address */
3057  MmspSortSegments(ImageSectionObject, Flags);
3058 
3059  /* Ensure they don't overlap in memory */
3060  if (!MmspCheckSegmentBounds(ImageSectionObject, Flags))
3062 
3063  /* Ensure they are aligned */
3064  OldNrSegments = ImageSectionObject->NrSegments;
3065 
3066  if (!MmspPageAlignSegments(ImageSectionObject, Flags))
3068 
3069  /* Trim them if the alignment phase merged some of them */
3070  if (ImageSectionObject->NrSegments < OldNrSegments)
3071  {
3072  PMM_SECTION_SEGMENT Segments;
3073  SIZE_T SizeOfSegments;
3074 
3075  SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * ImageSectionObject->NrSegments;
3076 
3077  Segments = ExAllocatePoolWithTag(PagedPool,
3078  SizeOfSegments,
3080 
3081  if (Segments == NULL)
3083 
3084  RtlCopyMemory(Segments, ImageSectionObject->Segments, SizeOfSegments);
3085  ExFreePool(ImageSectionObject->Segments);
3086  ImageSectionObject->Segments = Segments;
3087  }
3088 
3089  /* And finish their initialization */
3090  for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3091  {
3092  ExInitializeFastMutex(&ImageSectionObject->Segments[i].Lock);
3093  ImageSectionObject->Segments[i].ReferenceCount = &ImageSectionObject->RefCount;
3094  ImageSectionObject->Segments[i].Flags = &ImageSectionObject->SegFlags;
3095  MiInitializeSectionPageTable(&ImageSectionObject->Segments[i]);
3096  ImageSectionObject->Segments[i].FileObject = FileObject;
3097  }
3098 
3099  ASSERT(ImageSectionObject->RefCount > 0);
3100 
3101  ImageSectionObject->FileObject = FileObject;
3102 
3104  return Status;
3105 }
3106 
3107 NTSTATUS
3111  PLARGE_INTEGER UMaximumSize,
3115 {
3116  PSECTION Section;
3117  NTSTATUS Status;
3118  PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3119  KIRQL OldIrql;
3120 
3121 
3122  if (FileObject == NULL)
3124 
3125  if (FileObject->SectionObjectPointer == NULL)
3126  {
3127  DPRINT1("Denying section creation due to missing cache initialization\n");
3129  }
3130 
3131  /*
3132  * Create the section
3133  */
3138  NULL,
3139  sizeof(*Section),
3140  0,
3141  0,
3142  (PVOID*)(PVOID)&Section);
3143  if (!NT_SUCCESS(Status))
3144  {
3145  return Status;
3146  }
3147 
3148  /*
3149  * Initialize it
3150  */
3151  RtlZeroMemory(Section, sizeof(*Section));
3152 
3153  /* Mark this as a "ROS" Section */
3154  Section->u.Flags.filler = 1;
3155 
3156  Section->InitialPageProtection = SectionPageProtection;
3157  Section->u.Flags.File = 1;
3158  Section->u.Flags.Image = 1;
3160  Section->u.Flags.NoChange = 1;
3161 
3163 
3164  /* Wait for it to be properly created or deleted */
3165  ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
3166  while(ImageSectionObject && (ImageSectionObject->SegFlags & (MM_SEGMENT_INDELETE | MM_SEGMENT_INCREATE)))
3167  {
3169 
3171 
3173  ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
3174  }
3175 
3176  if (ImageSectionObject == NULL)
3177  {
3178  NTSTATUS StatusExeFmt;
3179 
3180  ImageSectionObject = ExAllocatePoolZero(NonPagedPool, sizeof(MM_IMAGE_SECTION_OBJECT), TAG_MM_SECTION_SEGMENT);
3181  if (ImageSectionObject == NULL)
3182  {
3184  ObDereferenceObject(Section);
3185  return STATUS_NO_MEMORY;
3186  }
3187 
3188  ImageSectionObject->SegFlags = MM_SEGMENT_INCREATE;
3189  ImageSectionObject->RefCount = 1;
3190  FileObject->SectionObjectPointer->ImageSectionObject = ImageSectionObject;
3191 
3193 
3194  /* Purge the cache */
3195  CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, NULL);
3196 
3197  StatusExeFmt = ExeFmtpCreateImageSection(FileObject, ImageSectionObject);
3198 
3199  if (!NT_SUCCESS(StatusExeFmt))
3200  {
3201  /* Unset */
3203  FileObject->SectionObjectPointer->ImageSectionObject = NULL;
3205 
3206  if(ImageSectionObject->Segments != NULL)
3207  ExFreePool(ImageSectionObject->Segments);
3208 
3209  /*
3210  * If image file is empty, then return that the file is invalid for section
3211  */
3212  Status = StatusExeFmt;
3213  if (StatusExeFmt == STATUS_END_OF_FILE)
3214  {
3216  }
3217 
3218  ExFreePoolWithTag(ImageSectionObject, TAG_MM_SECTION_SEGMENT);
3219  ObDereferenceObject(Section);
3220  return Status;
3221  }
3222 
3223  Section->Segment = (PSEGMENT)ImageSectionObject;
3224  ASSERT(ImageSectionObject->Segments);
3225  ASSERT(ImageSectionObject->RefCount > 0);
3226 
3227  /*
3228  * Lock the file
3229  */
3231  if (!NT_SUCCESS(Status))
3232  {
3233  /* Unset */
3235  FileObject->SectionObjectPointer->ImageSectionObject = NULL;
3237 
3238  ExFreePool(ImageSectionObject->Segments);
3239  ExFreePool(ImageSectionObject);
3240  ObDereferenceObject(Section);
3241  return Status;
3242  }
3243 
3245  ImageSectionObject->SegFlags &= ~MM_SEGMENT_INCREATE;
3246 
3247  /* Take a ref on the file on behalf of the newly created structure */
3249 
3251 
3252  Status = StatusExeFmt;
3253  }
3254  else
3255  {
3256  /* Take one ref */
3257  ImageSectionObject->RefCount++;
3258 
3260 
3261  Section->Segment = (PSEGMENT)ImageSectionObject;
3262 
3264  }
3265  //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
3266  *SectionObject = Section;
3267  ASSERT(ImageSectionObject->RefCount > 0);
3268 
3269  return Status;
3270 }
3271 
3272 
3273 
3274 static NTSTATUS
3277  BOOLEAN AsImage,
3279  PVOID* BaseAddress,
3280  SIZE_T ViewSize,
3281  ULONG Protect,
3282  LONGLONG ViewOffset,
3284 {
3285  PMEMORY_AREA MArea;
3286  NTSTATUS Status;
3287  ULONG Granularity;
3288 
3289  ASSERT(ViewSize != 0);
3290 
3291  if (Segment->WriteCopy)
3292  {
3293  /* We have to do this because the not present fault
3294  * and access fault handlers depend on the protection
3295  * that should be granted AFTER the COW fault takes
3296  * place to be in Region->Protect. The not present fault
3297  * handler changes this to the correct protection for COW when
3298  * mapping the pages into the process's address space. If a COW
3299  * fault takes place, the access fault handler sets the page protection
3300  * to these values for the newly copied pages
3301  */
3302  if (Protect == PAGE_WRITECOPY)
3304  else if (Protect == PAGE_EXECUTE_WRITECOPY)
3306  }
3307 
3308  if (*BaseAddress == NULL)
3309  Granularity = MM_ALLOCATION_GRANULARITY;
3310  else
3311  Granularity = PAGE_SIZE;
3312 
3313 #ifdef NEWCC
3314  if (Segment->Flags & MM_DATAFILE_SEGMENT)
3315  {
3317  FileOffset.QuadPart = ViewOffset;
3318  ObReferenceObject(Section);
3320  }
3321 #endif
3324  BaseAddress,
3325  ViewSize,
3326  Protect,
3327  &MArea,
3329  Granularity);
3330  if (!NT_SUCCESS(Status))
3331  {
3332  DPRINT1("Mapping between 0x%p and 0x%p failed (%X).\n",
3333  (*BaseAddress), (char*)(*BaseAddress) + ViewSize, Status);
3334  return Status;
3335  }
3336 
3337  InterlockedIncrement64(Segment->ReferenceCount);
3338 
3339  MArea->SectionData.Segment = Segment;
3340  MArea->SectionData.ViewOffset = ViewOffset;
3341  if (AsImage)
3342  {
3344  }
3345 
3346  MmInitializeRegion(&MArea->SectionData.RegionListHead,
3347  ViewSize, 0, Protect);
3348 
3349  return STATUS_SUCCESS;
3350 }
3351 
3352 
3353 static VOID
3355  PFN_NUMBER Page, SWAPENTRY SwapEntry, BOOLEAN Dirty)
3356 {
3357  ULONG_PTR Entry;
3359  SWAPENTRY SavedSwapEntry;
3363 
3366 
3368 
3370  MemoryArea->SectionData.ViewOffset;
3371 
3372  Segment = MemoryArea->SectionData.Segment;
3373 
3375  while (Entry && MM_IS_WAIT_PTE(Entry))
3376  {
3379 
3380  YieldProcessor();
3381 
3385  }
3386 
3387  /*
3388  * For a dirty, datafile, non-private page, there shoulkd be no swap entry
3389  */
3390  if (*Segment->Flags & MM_DATAFILE_SEGMENT)
3391  {
3392  if (Page == PFN_FROM_SSE(Entry) && Dirty)
3393  {
3394  ASSERT(SwapEntry == 0);
3395  }
3396  }
3397 
3398  if (SwapEntry != 0)
3399  {
3400  /*
3401  * Sanity check
3402  */
3403  MmFreeSwapPage(SwapEntry);
3404  }
3405  else if (Page != 0)
3406  {
3407  if (IS_SWAP_FROM_SSE(Entry) ||
3408  Page != PFN_FROM_SSE(Entry))
3409  {
3410  ASSERT(Process != NULL);
3411 
3412  /*
3413  * Just dereference private pages
3414  */
3415  SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
3416  if (SavedSwapEntry != 0)
3417  {
3418  MmFreeSwapPage(SavedSwapEntry);
3420  }
3423  }
3424  else
3425  {
3426  if (Process)
3427  {
3429  }
3430 
3431  /* We don't dirtify for System Space Maps. We let Cc manage that */
3433  }
3434  }
3435 }
3436 
3437 static NTSTATUS
3440 {
3441  NTSTATUS Status;
3444  PLIST_ENTRY CurrentEntry;
3445  PMM_REGION CurrentRegion;
3446  PLIST_ENTRY RegionListHead;
3447 
3449  BaseAddress);
3450  if (MemoryArea == NULL)
3451  {
3452  return STATUS_UNSUCCESSFUL;
3453  }
3454 
3455  Segment = MemoryArea->SectionData.Segment;
3456 
3457 #ifdef NEWCC
3458  if (Segment->Flags & MM_DATAFILE_SEGMENT)
3459  {
3463 
3464  return Status;
3465  }
3466 #endif
3467 
3469 
3471 
3472  RegionListHead = &MemoryArea->SectionData.RegionListHead;
3473  while (!IsListEmpty(RegionListHead))
3474  {
3475  CurrentEntry = RemoveHeadList(RegionListHead);
3476  CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION, RegionListEntry);
3477  ExFreePoolWithTag(CurrentRegion, TAG_MM_REGION);
3478  }
3479 
3480  if ((*Segment->Flags) & MM_PHYSICALMEMORY_SEGMENT)
3481  {
3483  MemoryArea,
3484  NULL,
3485  NULL);
3486  }
3487  else
3488  {
3490  MemoryArea,
3492  AddressSpace);
3493  }
3496  return Status;
3497 }
3498 
3499 /* This functions must be called with a locked address space */
3500 NTSTATUS
3501 NTAPI
3504  IN BOOLEAN SkipDebuggerNotify)
3505 {
3506  NTSTATUS Status;
3509  PVOID ImageBaseAddress = 0;
3510 
3511  DPRINT("Opening memory area Process %p BaseAddress %p\n",
3512  Process, BaseAddress);
3513 
3514  ASSERT(Process);
3515 
3517 
3519  BaseAddress);
3520  if (MemoryArea == NULL ||
3521 #ifdef NEWCC
3522  ((MemoryArea->Type != MEMORY_AREA_SECTION_VIEW) && (MemoryArea->Type != MEMORY_AREA_CACHE)) ||
3523 #else
3525 #endif
3527 
3528  {
3530 
3531  DPRINT1("Unable to find memory area at address %p.\n", BaseAddress);
3532  return STATUS_NOT_MAPPED_VIEW;
3533  }
3534 
3536  {
3537  ULONG i;
3538  ULONG NrSegments;
3539  PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3540  PMM_SECTION_SEGMENT SectionSegments;
3542 
3543  Segment = MemoryArea->SectionData.Segment;
3544  ImageSectionObject = ImageSectionObjectFromSegment(Segment);
3545  SectionSegments = ImageSectionObject->Segments;
3546  NrSegments = ImageSectionObject->NrSegments;
3547 
3549 
3550  /* Search for the current segment within the section segments
3551  * and calculate the image base address */
3552  for (i = 0; i < NrSegments; i++)
3553  {
3554  if (Segment == &SectionSegments[i])
3555  {
3556  ImageBaseAddress = (char*)BaseAddress - (ULONG_PTR)SectionSegments[i].Image.VirtualAddress;
3557  break;
3558  }
3559  }
3560  if (i >= NrSegments)
3561  {
3562  KeBugCheck(MEMORY_MANAGEMENT);
3563  }
3564 
3565  for (i = 0; i < NrSegments; i++)
3566  {
3567  PVOID SBaseAddress = (PVOID)
3568  ((char*)ImageBaseAddress + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
3569 
3570  Status = MmUnmapViewOfSegment(AddressSpace, SBaseAddress);
3571  if (!NT_SUCCESS(Status))
3572  {
3573  DPRINT1("MmUnmapViewOfSegment failed for %p (Process %p) with %lx\n",
3574  SBaseAddress, Process, Status);
3576  }
3577  }
3578  }
3579  else
3580  {
3582  if (!NT_SUCCESS(Status))
3583  {
3584  DPRINT1("MmUnmapViewOfSegment failed for %p (Process %p) with %lx\n",
3587  }
3588  }
3589 
3590  /* Notify debugger */
3591  if (ImageBaseAddress && !SkipDebuggerNotify) DbgkUnMapViewOfSection(ImageBaseAddress);
3592 
3593  return STATUS_SUCCESS;
3594 }
3595 
3596 
3597 
3598 
3621 NTSTATUS
3622 NTAPI
3624  _In_ HANDLE SectionHandle,
3625  _In_ SECTION_INFORMATION_CLASS SectionInformationClass,
3626  _Out_ PVOID SectionInformation,
3627  _In_ SIZE_T SectionInformationLength,
3629 {
3630  PSECTION Section;
3632  NTSTATUS Status;
3633  PAGED_CODE();
3634 
3636  if (PreviousMode != KernelMode)
3637  {
3638  _SEH2_TRY
3639  {
3640  ProbeForWrite(SectionInformation,
3641  SectionInformationLength,
3642  __alignof(ULONG));
3643  if (ResultLength != NULL)
3644  {
3646  sizeof(*ResultLength),
3647  __alignof(SIZE_T));
3648  }
3649  }
3651  {
3653  }
3654  _SEH2_END;
3655  }
3656 
3657  if (SectionInformationClass == SectionBasicInformation)
3658  {
3659  if (SectionInformationLength < sizeof(SECTION_BASIC_INFORMATION))
3660  {
3662  }
3663  }
3664  else if (SectionInformationClass == SectionImageInformation)
3665  {
3666  if (SectionInformationLength < sizeof(SECTION_IMAGE_INFORMATION))
3667  {
3669  }
3670  }
3671  else
3672  {
3674  }
3675 
3676  Status = ObReferenceObjectByHandle(SectionHandle,
3677  SECTION_QUERY,
3679  PreviousMode,
3680  (PVOID*)(PVOID)&Section,
3681  NULL);
3682  if (!NT_SUCCESS(Status))
3683  {
3684  DPRINT1("Failed to reference section: 0x%lx\n", Status);
3685  return Status;
3686  }
3687 
3688  switch(SectionInformationClass)
3689  {
3691  {
3693 
3694  Sbi.Size = Section->SizeOfSection;
3695  Sbi.BaseAddress = (PVOID)Section->Address.StartingVpn;
3696 
3697  Sbi.Attributes = 0;
3698  if (Section->u.Flags.File)
3699  Sbi.Attributes |= SEC_FILE;
3700  if (Section->u.Flags.Image)
3701  Sbi.Attributes |= SEC_IMAGE;
3702 
3703  /* Those are not set *************
3704  if (Section->u.Flags.Commit)
3705  Sbi.Attributes |= SEC_COMMIT;
3706  if (Section->u.Flags.Reserve)
3707  Sbi.Attributes |= SEC_RESERVE;
3708  **********************************/
3709 
3710  if (Section->u.Flags.Image)
3711  {
3712  if (MiIsRosSectionObject(Section))
3713  {
3714  PMM_IMAGE_SECTION_OBJECT ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
3715  Sbi.BaseAddress = 0;
3716  Sbi.Size.QuadPart = ImageSectionObject->ImageInformation.ImageFileSize;
3717  }
3718  else
3719  {
3720  /* Not supported yet */
3721  ASSERT(FALSE);
3722  }
3723  }
3724  else if (MiIsRosSectionObject(Section))
3725  {
3726  Sbi.BaseAddress = (PVOID)((PMM_SECTION_SEGMENT)Section->Segment)->Image.VirtualAddress;
3727  Sbi.Size.QuadPart = ((PMM_SECTION_SEGMENT)Section->Segment)->RawLength.QuadPart;
3728  }
3729  else
3730  {
3731  DPRINT1("Unimplemented code path!");
3732  }
3733 
3734  _SEH2_TRY
3735  {
3736  *((SECTION_BASIC_INFORMATION*)SectionInformation) = Sbi;
3737  if (ResultLength != NULL)
3738  {
3739  *ResultLength = sizeof(Sbi);
3740  }
3741  }
3743  {
3745  }
3746  _SEH2_END;
3747  break;
3748  }
3750  {
3751  if (!Section->u.Flags.Image)
3752  {
3754  }
3755  else if (MiIsRosSectionObject(Section))
3756  {
3757  PMM_IMAGE_SECTION_OBJECT ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
3758 
3759  _SEH2_TRY
3760  {
3761  PSECTION_IMAGE_INFORMATION Sii = (PSECTION_IMAGE_INFORMATION)SectionInformation;
3762  *Sii = ImageSectionObject->ImageInformation;
3763  if (ResultLength != NULL)
3764  {
3765  *ResultLength = sizeof(*Sii);
3766  }
3767  }
3769  {
3771  }
3772  _SEH2_END;
3773  }
3774  else
3775  {
3776  _SEH2_TRY
3777  {
3778  PSECTION_IMAGE_INFORMATION Sii = (PSECTION_IMAGE_INFORMATION)SectionInformation;
3779  *Sii = *Section->Segment->u2.ImageInformation;
3780  if (ResultLength != NULL)
3781  *ResultLength = sizeof(*Sii);
3782  }
3784  {
3786  }
3787  _SEH2_END;
3788  }
3789  break;
3790  }
3791  default:
3792  DPRINT1("Unknown SectionInformationClass: %d\n", SectionInformationClass);
3794  }
3795 
3796  ObDereferenceObject(Section);
3797 
3798  return Status;
3799 }
3800 
3801 /**********************************************************************
3802  * NAME EXPORTED
3803  * MmMapViewOfSection
3804  *
3805  * DESCRIPTION
3806  * Maps a view of a section into the virtual address space of a
3807  * process.
3808  *
3809  * ARGUMENTS
3810  * Section
3811  * Pointer to the section object.
3812  *
3813  * ProcessHandle
3814  * Pointer to the process.
3815  *
3816  * BaseAddress
3817  * Desired base address (or NULL) on entry;
3818  * Actual base address of the view on exit.
3819  *
3820  * ZeroBits
3821  * Number of high order address bits that must be zero.
3822  *
3823  * CommitSize
3824  * Size in bytes of the initially committed section of
3825  * the view.
3826  *
3827  * SectionOffset
3828  * Offset in bytes from the beginning of the section
3829  * to the beginning of the view.
3830  *
3831  * ViewSize
3832  * Desired length of map (or zero to map all) on entry
3833  * Actual length mapped on exit.
3834  *
3835  * InheritDisposition
3836  * Specified how the view is to be shared with
3837  * child processes.
3838  *
3839  * AllocationType
3840  * Type of allocation for the pages.
3841  *
3842  * Protect
3843  * Protection for the committed region of the view.
3844  *
3845  * RETURN VALUE
3846  * Status.
3847  *
3848  * @implemented
3849  */
3860  IN ULONG Protect)
3861 {
3862  PSECTION Section;
3865  BOOLEAN NotAtBase = FALSE;
3866 
3868  {
3869  DPRINT("Mapping ARM3 section into %s\n", Process->ImageFileName);
3871  Process,
3872  BaseAddress,
3873  ZeroBits,
3874  CommitSize,
3875  SectionOffset,
3876  ViewSize,
3879  Protect);
3880  }
3881 
3882  ASSERT(Process);
3883 
3885  {
3887  }
3888 
3889  /* FIXME: We should keep this, but it would break code checking equality */
3890  Protect &= ~PAGE_NOCACHE;
3891 
3892  Section = SectionObject;
3893  AddressSpace = &Process->Vm;
3894 
3895  if (Section->u.Flags.NoChange)
3897 
3899 
3900  if (Section->u.Flags.Image)
3901  {
3902  ULONG i;
3903  ULONG NrSegments;
3904  ULONG_PTR ImageBase;
3905  SIZE_T ImageSize;
3906  PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3907  PMM_SECTION_SEGMENT SectionSegments;
3908 
3909  ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
3910  SectionSegments = ImageSectionObject->Segments;
3911  NrSegments = ImageSectionObject->NrSegments;
3912 
3913  ASSERT(ImageSectionObject->RefCount > 0);
3914 
3915  ImageBase = (ULONG_PTR)*BaseAddress;
3916  if (ImageBase == 0)
3917  {
3918  ImageBase = (ULONG_PTR)ImageSectionObject->BasedAddress;
3919  }
3920 
3921  ImageSize = 0;
3922  for (i = 0; i < NrSegments; i++)
3923  {
3924  ULONG_PTR MaxExtent;
3925  MaxExtent = (ULONG_PTR)(SectionSegments[i].Image.VirtualAddress +
3926  SectionSegments[i].Length.QuadPart);
3927  ImageSize = max(ImageSize, MaxExtent);
3928  }
3929 
3930  ImageSectionObject->ImageInformation.ImageFileSize = (ULONG)ImageSize;
3931 
3932  /* Check for an illegal base address */
3933  if (((ImageBase + ImageSize) > (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS) ||
3934  ((ImageBase + ImageSize) < ImageSize))
3935  {
3936  ASSERT(*BaseAddress == NULL);
3937  ImageBase = ALIGN_DOWN_BY((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - ImageSize,
3939  NotAtBase = TRUE;
3940  }
3941  else if (ImageBase != ALIGN_DOWN_BY(ImageBase, MM_VIRTMEM_GRANULARITY))
3942  {
3943  ASSERT(*BaseAddress == NULL);
3944  ImageBase = ALIGN_DOWN_BY(ImageBase, MM_VIRTMEM_GRANULARITY);
3945  NotAtBase = TRUE;
3946  }
3947 
3948  /* Check there is enough space to map the section at that point. */
3950  PAGE_ROUND_UP(ImageSize)) != NULL)
3951  {
3952  /* Fail if the user requested a fixed base address. */
3953  if ((*BaseAddress) != NULL)
3954  {
3957  }
3958  /* Otherwise find a gap to map the image. */
3960  if (ImageBase == 0)
3961  {
3964  }
3965  /* Remember that we loaded image at a different base address */
3966  NotAtBase = TRUE;
3967  }
3968 
3969  for (i = 0; i < NrSegments; i++)
3970  {
3971  PVOID SBaseAddress = (PVOID)
3972  ((char*)ImageBase + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
3973  MmLockSectionSegment(&SectionSegments[i]);
3975  TRUE,
3976  &SectionSegments[i],
3977  &SBaseAddress,
3978  SectionSegments[i].Length.QuadPart,
3979  SectionSegments[i].Protection,
3980  0,
3981  0);
3982  MmUnlockSectionSegment(&SectionSegments[i]);
3983  if (!NT_SUCCESS(Status))
3984  {
3985  /* roll-back */
3986  while (i--)
3987  {
3988  SBaseAddress = ((char*)ImageBase + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
3989  MmLockSectionSegment(&SectionSegments[i]);
3990  MmUnmapViewOfSegment(AddressSpace, SBaseAddress);
3991  MmUnlockSectionSegment(&SectionSegments[i]);
3992  }
3993 
3995  return Status;
3996  }
3997  }
3998 
3999  *BaseAddress = (PVOID)ImageBase;
4000  *ViewSize = ImageSize;
4001  }
4002  else
4003  {
4005  LONGLONG ViewOffset;
4006 
4007  ASSERT(Segment->RefCount > 0);
4008 
4009  /* check for write access */
4012  {
4015  }
4016  /* check for read access */
4019  {
4022  }
4023  /* check for execute access */
4026  {
4029  }
4030 
4031  if (SectionOffset == NULL)
4032  {
4033  ViewOffset = 0;
4034  }
4035  else
4036  {
4037  ViewOffset = SectionOffset->QuadPart;
4038  }
4039 
4040  if ((ViewOffset % PAGE_SIZE) != 0)
4041  {
4043  return STATUS_MAPPED_ALIGNMENT;
4044  }
4045 
4046  if ((*ViewSize) == 0)
4047  {
4048  (*ViewSize) = Section->SizeOfSection.QuadPart - ViewOffset;
4049  }
4050  else if ((ExGetPreviousMode() == UserMode) &&
4051  (((*ViewSize)+ViewOffset) > Section->SizeOfSection.QuadPart) &&
4052  (!Section->u.Flags.Reserve))
4053  {
4054  /* Dubious */
4055  (*ViewSize) = MIN(Section->SizeOfSection.QuadPart - ViewOffset, SIZE_T_MAX - PAGE_SIZE);
4056  }
4057 
4059 
4062  FALSE,
4063  Segment,
4064  BaseAddress,
4065  *ViewSize,
4066  Protect,
4067  ViewOffset,
4070  if (!NT_SUCCESS(Status))
4071  {
4073  return Status;
4074  }
4075  }
4076 
4078 
4079  if (NotAtBase)
4081  else
4083 
4084  return Status;
4085 }
4086 
4087 /*
4088  * @unimplemented
4089  */
4090 BOOLEAN NTAPI
4093 {
4094  BOOLEAN Ret;
4096 
4097  /* Check whether an ImageSectionObject exists */
4098  if (SectionObjectPointer->ImageSectionObject != NULL)
4099  {
4100  DPRINT1("ERROR: File can't be truncated because it has an image section\n");
4101  return FALSE;
4102  }
4103 
4105  if (!Segment)
4106  {
4107  /* There is no data section. It's fine to do anything. */
4108  return TRUE;
4109  }
4110 
4112  if ((Segment->SectionCount == 0) ||
4113  ((Segment->SectionCount == 1) && (SectionObjectPointer->SharedCacheMap != NULL)))
4114  {
4115  /* If the cache is the only one holding a reference to the segment, then it's fine to resize */
4116  Ret = TRUE;
4117  }
4118  else
4119  {
4120  /* We can't shrink, but we can extend */
4121  Ret = NewFileSize->QuadPart >= Segment->RawLength.QuadPart;
4122 #if DBG
4123  if (!Ret)
4124  {
4125  DPRINT1("Cannot truncate data: New Size %I64d, Segment Size %I64d\n", NewFileSize->QuadPart, Segment->RawLength.QuadPart);
4126  }
4127 #endif
4128  }
4131 
4132  DPRINT("FIXME: didn't check for outstanding write probes\n");
4133 
4134  return Ret;
4135 }
4136 
4137 /*
4138  * @implemented
4139  */
4140 BOOLEAN NTAPI
4143 {
4144  switch(FlushType)
4145  {
4146  case MmFlushForDelete:
4147  case MmFlushForWrite:
4148  {
4149  BOOLEAN Ret = TRUE;
4151 
4152  if (SectionObjectPointer->ImageSectionObject)
4153  {
4154  PMM_IMAGE_SECTION_OBJECT ImageSectionObject = SectionObjectPointer->ImageSectionObject;
4155  if (!(ImageSectionObject->SegFlags & MM_SEGMENT_INDELETE))
4156  Ret = FALSE;
4157  }
4158 
4160  return Ret;
4161  }
4162  }
4163  return FALSE;
4164 }
4165 
4166 /*
4167  * @implemented
4168  */
4169 NTSTATUS
4170 NTAPI
4172  OUT PVOID * MappedBase,
4174 {
4176 
4177  SectionOffset.QuadPart = 0;
4178 
4180 }
4181 
4182 NTSTATUS
4183 NTAPI
4190  )
4191 {
4192  PSECTION Section = SectionObject;
4195  NTSTATUS Status;
4196 
4198 
4199  PAGED_CODE();
4200 
4202  {
4204  &MmSession,
4205  MappedBase,
4206  ViewSize,
4207  SectionOffset);
4208  }
4209 
4210  DPRINT("MmMapViewInSystemSpaceEx() called\n");
4211 
4212  Section = SectionObject;
4213  Segment = (PMM_SECTION_SEGMENT)Section->Segment;
4214 
4215  if (*ViewSize == 0)
4216  {
4217  LONGLONG MapSizeLL;
4218 
4219  /* Page-align the mapping */
4220  SectionOffset->LowPart = PAGE_ROUND_DOWN(SectionOffset->LowPart);
4221 
4222  if (!NT_SUCCESS(RtlLongLongSub(Section->SizeOfSection.QuadPart, SectionOffset->QuadPart, &MapSizeLL)))
4223  return STATUS_INVALID_VIEW_SIZE;
4224 
4225  if (!NT_SUCCESS(RtlLongLongToSIZET(MapSizeLL, ViewSize)))
4226  return STATUS_INVALID_VIEW_SIZE;
4227  }
4228  else
4229  {
4230  LONGLONG HelperLL;
4231 
4232  /* Get the map end */
4233  if (!NT_SUCCESS(RtlLongLongAdd(SectionOffset->QuadPart, *ViewSize, &HelperLL)))
4234  return STATUS_INVALID_VIEW_SIZE;
4235 
4236  /* Round it up, if needed */
4237  if (HelperLL % PAGE_SIZE)
4238  {
4239  if (!NT_SUCCESS(RtlLongLongAdd(HelperLL, PAGE_SIZE - (HelperLL % PAGE_SIZE), &HelperLL)))
4240  return STATUS_INVALID_VIEW_SIZE;
4241  }
4242 
4243  /* Now that we have the mapping end, we can align down its start */
4244  SectionOffset->LowPart = PAGE_ROUND_DOWN(SectionOffset->LowPart);
4245 
4246  /* Get the new size */
4247  if (!NT_SUCCESS(RtlLongLongSub(HelperLL, SectionOffset->QuadPart, &HelperLL)))
4248  return STATUS_INVALID_VIEW_SIZE;
4249 
4250  if (!NT_SUCCESS(RtlLongLongToSIZET(HelperLL, ViewSize)))
4251  return STATUS_INVALID_VIEW_SIZE;
4252  }
4253 
4255 
4257 
4259 
4261  Section->u.Flags.Image,
4262  Segment,
4263  MappedBase,
4264  *ViewSize,
4266  SectionOffset->QuadPart,
4267  SEC_RESERVE);
4268 
4271 
4272  return Status;
4273 }
4274 
4275 /* This function must be called with adress space lock held */
4276 NTSTATUS
4277 NTAPI
4279 {
4280  DPRINT("MmUnmapViewInSystemSpace() called\n");
4281 
4283 }
4284 
4285 /**********************************************************************
4286  * NAME EXPORTED
4287  * MmCreateSection@
4288  *
4289  * DESCRIPTION
4290  * Creates a section object.
4291  *
4292  * ARGUMENTS
4293  * SectionObject (OUT)
4294  * Caller supplied storage for the resulting pointer
4295  * to a SECTION_OBJECT instance;
4296  *
4297  * DesiredAccess
4298  * Specifies the desired access to the section can be a
4299  * combination of:
4300  * STANDARD_RIGHTS_REQUIRED |
4301  * SECTION_QUERY |
4302  * SECTION_MAP_WRITE |
4303  * SECTION_MAP_READ |
4304  * SECTION_MAP_EXECUTE
4305  *
4306  * ObjectAttributes [OPTIONAL]
4307  * Initialized attributes for the object can be used
4308  * to create a named section;
4309  *
4310  * MaximumSize
4311  * Maximizes the size of the memory section. Must be
4312  * non-NULL for a page-file backed section.
4313  * If value specified for a mapped file and the file is
4314  * not large enough, file will be extended.
4315  *
4316  * SectionPageProtection
4317  * Can be a combination of:
4318  * PAGE_READONLY |
4319  * PAGE_READWRITE |
4320  * PAGE_WRITEONLY |
4321  * PAGE_WRITECOPY
4322  *
4323  * AllocationAttributes
4324  * Can be a combination of:
4325  * SEC_IMAGE |
4326  * SEC_RESERVE
4327  *
4328  * FileHandle
4329  * Handle to a file to create a section mapped to a file
4330  * instead of a memory backed section;
4331  *
4332  * File
4333  * Unknown.
4334  *
4335  * RETURN VALUE
4336  * Status.
4337  *
4338  * @implemented
4339  */
4349 {
4350  NTSTATUS Status;
4351  ULONG Protection;
4352  PSECTION *SectionObject = (PSECTION *)Section;
4353  BOOLEAN FileLock = FALSE;
4354 
4355  /* Check if an ARM3 section is being created instead */
4357  {
4358  if (!(FileObject) && !(FileHandle))
4359  {
4360  return MmCreateArm3Section(Section,
4361  DesiredAccess,
4363  MaximumSize,
4365  AllocationAttributes &~ 1,
4366  FileHandle,
4367  FileObject);
4368  }
4369  }
4370 
4371  /* Convert section flag to page flag */
4373 
4374  /* Check to make sure the protection is correct. Nt* does this already */
4376  if (Protection == MM_INVALID_PROTECTION)
4377  {
4378  DPRINT1("Page protection is invalid\n");
4380  }
4381 
4382  /* Check if this is going to be a data or image backed file section */
4383  if ((FileHandle) || (FileObject))
4384  {
4385  /* These cannot be mapped with large pages */
4387  {
4388  DPRINT1("Large pages cannot be used with an image mapping\n");
4390  }
4391 
4392  /* Did the caller pass a file object ? */
4393  if (FileObject)
4394  {
4395  /* Reference the object directly */
4397 
4398  /* We don't create image mappings with file objects */
4400  }
4401  else
4402  {
4403  /* Reference the file handle to get the object */
4405  MmMakeFileAccess[Protection],
4408  (PVOID*)&FileObject,
4409  NULL);
4410  if (!NT_SUCCESS(Status))
4411  {
4412  DPRINT1("Failed to get a handle to the FO: %lx\n", Status);
4413  return Status;
4414  }
4415 
4416  /* Lock the file */
4418  if (!NT_SUCCESS(Status))
4419  {
4421  return Status;
4422  }
4423 
4424  FileLock = TRUE;
4425 
4426  /* Deny access if there are writes on the file */
4427 #if 0
4429  {
4430  DPRINT1("Cannot create image maps with writers open on the file!\n");
4432  goto Quit;
4433  }
4434 #else
4436  DPRINT1("Creating image map with writers open on the file!\n");
4437 #endif
4438  }
4439  }
4440  else
4441  {
4442  /* A handle must be supplied with SEC_IMAGE, as this is the no-handle path */
4444  }
4445 
4447  {
4449  DesiredAccess,
4451  MaximumSize,
4454  FileObject);
4455  }
4456 #ifndef NEWCC
4457  else if (FileObject != NULL)
4458  {
4460  DesiredAccess,
4462  MaximumSize,
4465  FileObject,
4466  FileHandle != NULL);
4467  }
4468 #else
4469  else if (FileHandle != NULL || FileObject != NULL)
4470  {
4472  DesiredAccess,
4474  MaximumSize,
4477  FileObject);
4478  }
4479 #endif
4480  else
4481  {
4482  /* All cases should be handled above */
4484  }
4485 
4486  if (FileLock)
4488  if (FileObject)
4490 
4491  return Status;
4492 }
4493 
4494 BOOLEAN
4495 NTAPI
4498  _In_ PVOID Address,
4499  _In_ ULONG Length)
4500 {
4502  BOOLEAN Ret = TRUE;
4504  LARGE_INTEGER SegmentOffset, RangeEnd;
4506 
4508 
4510  if (MemoryArea == NULL)
4511  {
4513  return FALSE;
4514  }
4515 
4516  /* Only supported in old Mm for now */
4518  /* For file mappings */
4520 
4521  Segment = MemoryArea->SectionData.Segment;
4523 
4525  + MemoryArea->SectionData.ViewOffset;
4527  + MemoryArea->SectionData.ViewOffset;
4528 
4529  while (SegmentOffset.QuadPart < RangeEnd.QuadPart)
4530  {
4532  if ((Entry == 0) || IS_SWAP_FROM_SSE(Entry))
4533  {
4534  Ret = FALSE;
4535  break;
4536  }
4537  SegmentOffset.QuadPart += PAGE_SIZE;
4538  }
4539 
4541 
4543  return Ret;
4544 }
4545 
4546 /* Like CcPurgeCache but for the in-memory segment */
4547 BOOLEAN
4548 NTAPI
4552  _In_ ULONG Length)
4553 {
4554  LARGE_INTEGER PurgeStart, PurgeEnd;
4556 
4558  if (!Segment)
4559  {
4560  /* Nothing to purge */
4561  return STATUS_SUCCESS;
4562  }
4563 
4564  PurgeStart.QuadPart = Offset ? Offset->QuadPart : 0LL;
4565  if (Length && Offset)
4566  {
4567  if (!NT_SUCCESS(RtlLongLongAdd(PurgeStart.QuadPart, Length, &PurgeEnd.QuadPart)))
4568  return FALSE;
4569  }
4570 
4572 
4573  if (!Length || !Offset)
4574  {
4575  /* We must calculate the length for ourselves */
4576  /* FIXME: All of this is suboptimal */
4577  ULONG ElemCount = RtlNumberGenericTableElements(&Segment->PageTable);
4578  /* No page. Nothing to purge */
4579  if (!ElemCount)
4580  {
4583  return TRUE;
4584  }
4585 
4587  PurgeEnd.QuadPart = PageTable->FileOffset.QuadPart + _countof(PageTable->PageEntries) * PAGE_SIZE;
4588  }
4589 
4590  while (PurgeStart.QuadPart < PurgeEnd.QuadPart)
4591  {
4593 
4594  if (Entry == 0)
4595  {
4596  PurgeStart.QuadPart += PAGE_SIZE;
4597  continue;
4598  }
4599 
4600  if (IS_SWAP_FROM_SSE(Entry))
4601  {
4602  ASSERT(SWAPENTRY_FROM_SSE(Entry) == MM_WAIT_ENTRY);
4603  /* The page is currently being read. Meaning someone will need it soon. Bad luck */
4606  return FALSE;
4607  }
4608 
4609  if (IS_WRITE_SSE(Entry))
4610  {
4611  /* We're trying to purge an entry which is being written. Restart this loop iteration */
4615  continue;
4616  }
4617 
4618  if (SHARE_COUNT_FROM_SSE(Entry) > 0)
4619  {
4620  /* This page is currently in use. Bad luck */
4623  return FALSE;
4624  }
4625 
4626  /* We can let this page go */
4627  MmSetPageEntrySectionSegment(Segment, &PurgeStart, 0);
4629 
4630  PurgeStart.QuadPart += PAGE_SIZE;
4631  }
4632 
4633  /* This page is currently in use. Bad luck */
4636  return TRUE;
4637 }
4638 
4639 NTSTATUS
4640 NTAPI
4644  _In_ ULONG Length,
4645  _In_ PLARGE_INTEGER ValidDataLength)
4646 {
4648 
4649  /* There must be a segment for this call */
4650  ASSERT(Segment);
4651 
4652  NTSTATUS Status = MmMakeSegmentResident(Segment, Offset, Length, ValidDataLength);
4653 
4655 
4656  return Status;
4657 }
4658 
4659 NTSTATUS
4660 NTAPI
4664  _In_ ULONG Length,
4666 {
4667  LARGE_INTEGER FlushStart, FlushEnd;
4668  NTSTATUS Status;
4669 
4670  if (Offset)
4671  {
4672  FlushStart = *Offset;
4673  Status = RtlLongLongAdd(FlushStart.QuadPart, Length, &FlushEnd.QuadPart);
4674  if (!NT_SUCCESS(Status))
4675  return Status;
4676  }
4677 
4678  if (Iosb)
4679  Iosb->Information = 0;
4680 
4682  if (!Segment)
4683  {
4684  /* Nothing to flush */
4685  if (Iosb)
4686  Iosb->Status = STATUS_SUCCESS;
4687  return STATUS_SUCCESS;
4688  }
4689 
4690  ASSERT(*Segment->Flags & MM_DATAFILE_SEGMENT);
4691 
4693 
4694  if (!Offset)
4695  {
4696  FlushStart.QuadPart = 0;
4697 
4698  /* FIXME: All of this is suboptimal */
4699  ULONG ElemCount = RtlNumberGenericTableElements(&Segment->PageTable);
4700  /* No page. Nothing to flush */
4701  if (!ElemCount)
4702  {
4705  if (Iosb)
4706  {
4707  Iosb->Status = STATUS_SUCCESS;
4708  Iosb->Information = 0;
4709  }
4710  return STATUS_SUCCESS;
4711  }
4712 
4714  FlushEnd.QuadPart = PageTable->FileOffset.QuadPart + _countof(PageTable->PageEntries) * PAGE_SIZE;
4715  }
4716 
4717  FlushStart.QuadPart >>= PAGE_SHIFT;
4718  FlushStart.QuadPart <<= PAGE_SHIFT;
4719 
4720  while (FlushStart.QuadPart < FlushEnd.QuadPart)
4721  {
4723 
4724  if (IS_DIRTY_SSE(Entry))
4725  {
4726  MmCheckDirtySegment(Segment, &FlushStart, FALSE, FALSE);
4727 
4728  if (Iosb)
4729  Iosb->Information += PAGE_SIZE;
4730  }
4731 
4732  FlushStart.QuadPart += PAGE_SIZE;
4733  }
4734 
4736 
4738 
4739  if (Iosb)
4740  Iosb->Status = STATUS_SUCCESS;
4741 
4742  return