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