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