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 * PURPOSE: Implements section objects
21 *
22 * PROGRAMMERS: Rex Jolliff
23 * David Welch
24 * Eric Kohl
25 * Emanuele Aliberti
26 * Eugene Ingerman
27 * Casper Hornstrup
28 * KJK::Hyperion
29 * Guido de Jong
30 * Ge van Geldorp
31 * Royce Mitchell III
32 * Filip Navara
33 * Aleksey Bragin
34 * Jason Filby
35 * Thomas Weidenmueller
36 * Gunnar Andre' Dalsnes
37 * Mike Nordell
38 * Alex Ionescu
39 * Gregor Anich
40 * Steven Edwards
41 * Herve Poussineau
42 */
43
44 /* INCLUDES *****************************************************************/
45
46 #include <ntoskrnl.h>
47 #include <cache/newcc.h>
48 #include <cache/section/newmm.h>
49 #define NDEBUG
50 #include <debug.h>
51 #include <reactos/exeformat.h>
52
53 #include "ARM3/miarm.h"
54
55 #undef MmSetPageEntrySectionSegment
56 #define MmSetPageEntrySectionSegment(S,O,E) do { \
57 DPRINT("SetPageEntrySectionSegment(old,%p,%x,%x)\n",(S),(O)->LowPart,E); \
58 _MmSetPageEntrySectionSegment((S),(O),(E),__FILE__,__LINE__); \
59 } while (0)
60
61 extern MMSESSION MmSession;
62
63 static LARGE_INTEGER TinyTime = {{-1L, -1L}};
64
65 #ifndef NEWCC
66 KEVENT MmWaitPageEvent;
67
68 VOID
69 NTAPI
_MmLockSectionSegment(PMM_SECTION_SEGMENT Segment,const char * file,int line)70 _MmLockSectionSegment(PMM_SECTION_SEGMENT Segment, const char *file, int line)
71 {
72 //DPRINT("MmLockSectionSegment(%p,%s:%d)\n", Segment, file, line);
73 ExAcquireFastMutex(&Segment->Lock);
74 Segment->Locked = TRUE;
75 }
76
77 VOID
78 NTAPI
_MmUnlockSectionSegment(PMM_SECTION_SEGMENT Segment,const char * file,int line)79 _MmUnlockSectionSegment(PMM_SECTION_SEGMENT Segment, const char *file, int line)
80 {
81 ASSERT(Segment->Locked);
82 Segment->Locked = FALSE;
83 ExReleaseFastMutex(&Segment->Lock);
84 //DPRINT("MmUnlockSectionSegment(%p,%s:%d)\n", Segment, file, line);
85 }
86 #endif
87
88 static
89 PMM_SECTION_SEGMENT
MiGrabDataSection(PSECTION_OBJECT_POINTERS SectionObjectPointer)90 MiGrabDataSection(PSECTION_OBJECT_POINTERS SectionObjectPointer)
91 {
92 KIRQL OldIrql = MiAcquirePfnLock();
93 PMM_SECTION_SEGMENT Segment = NULL;
94
95 while (TRUE)
96 {
97 Segment = SectionObjectPointer->DataSectionObject;
98 if (!Segment)
99 break;
100
101 if (Segment->SegFlags & (MM_SEGMENT_INCREATE | MM_SEGMENT_INDELETE))
102 {
103 MiReleasePfnLock(OldIrql);
104 KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
105 OldIrql = MiAcquirePfnLock();
106 continue;
107 }
108
109 ASSERT(Segment->SegFlags & MM_DATAFILE_SEGMENT);
110 InterlockedIncrement64(&Segment->RefCount);
111 break;
112 }
113
114 MiReleasePfnLock(OldIrql);
115
116 return Segment;
117 }
118
119 /* Somewhat grotesque, but eh... */
ImageSectionObjectFromSegment(PMM_SECTION_SEGMENT Segment)120 PMM_IMAGE_SECTION_OBJECT ImageSectionObjectFromSegment(PMM_SECTION_SEGMENT Segment)
121 {
122 ASSERT((Segment->SegFlags & MM_DATAFILE_SEGMENT) == 0);
123
124 return CONTAINING_RECORD(Segment->ReferenceCount, MM_IMAGE_SECTION_OBJECT, RefCount);
125 }
126
127 NTSTATUS
128 MiMapViewInSystemSpace(IN PVOID Section,
129 IN PVOID Session,
130 OUT PVOID *MappedBase,
131 IN OUT PSIZE_T ViewSize,
132 IN PLARGE_INTEGER SectionOffset);
133
134 NTSTATUS
135 NTAPI
136 MmCreateArm3Section(OUT PVOID *SectionObject,
137 IN ACCESS_MASK DesiredAccess,
138 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
139 IN PLARGE_INTEGER InputMaximumSize,
140 IN ULONG SectionPageProtection,
141 IN ULONG AllocationAttributes,
142 IN HANDLE FileHandle OPTIONAL,
143 IN PFILE_OBJECT FileObject OPTIONAL);
144
145 NTSTATUS
146 NTAPI
147 MmMapViewOfArm3Section(IN PVOID SectionObject,
148 IN PEPROCESS Process,
149 IN OUT PVOID *BaseAddress,
150 IN ULONG_PTR ZeroBits,
151 IN SIZE_T CommitSize,
152 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
153 IN OUT PSIZE_T ViewSize,
154 IN SECTION_INHERIT InheritDisposition,
155 IN ULONG AllocationType,
156 IN ULONG Protect);
157
158 //
159 // PeFmtCreateSection depends on the following:
160 //
161 C_ASSERT(EXEFMT_LOAD_HEADER_SIZE >= sizeof(IMAGE_DOS_HEADER));
162 C_ASSERT(sizeof(IMAGE_NT_HEADERS32) <= sizeof(IMAGE_NT_HEADERS64));
163
164 C_ASSERT(TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) == TYPE_ALIGNMENT(IMAGE_NT_HEADERS64));
165 C_ASSERT(RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader) == RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS64, FileHeader));
166 C_ASSERT(FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader) == FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader));
167
168 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, Magic));
169 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SectionAlignment));
170 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, FileAlignment));
171 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, Subsystem));
172 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, MinorSubsystemVersion));
173 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, MajorSubsystemVersion));
174 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, AddressOfEntryPoint));
175 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SizeOfCode));
176 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SizeOfHeaders));
177
178 /* TYPES *********************************************************************/
179
180 typedef struct
181 {
182 PMEMORY_AREA MemoryArea;
183 PMM_SECTION_SEGMENT Segment;
184 LARGE_INTEGER Offset;
185 BOOLEAN WasDirty;
186 BOOLEAN Private;
187 PEPROCESS CallingProcess;
188 ULONG_PTR SectionEntry;
189 }
190 MM_SECTION_PAGEOUT_CONTEXT;
191
192 /* GLOBALS *******************************************************************/
193
194 POBJECT_TYPE MmSectionObjectType = NULL;
195
196 ULONG_PTR MmSubsectionBase;
197
198 static ULONG SectionCharacteristicsToProtect[16] =
199 {
200 PAGE_NOACCESS, /* 0 = NONE */
201 PAGE_NOACCESS, /* 1 = SHARED */
202 PAGE_EXECUTE, /* 2 = EXECUTABLE */
203 PAGE_EXECUTE, /* 3 = EXECUTABLE, SHARED */
204 PAGE_READONLY, /* 4 = READABLE */
205 PAGE_READONLY, /* 5 = READABLE, SHARED */
206 PAGE_EXECUTE_READ, /* 6 = READABLE, EXECUTABLE */
207 PAGE_EXECUTE_READ, /* 7 = READABLE, EXECUTABLE, SHARED */
208 /*
209 * FIXME? do we really need the WriteCopy field in segments? can't we use
210 * PAGE_WRITECOPY here?
211 */
212 PAGE_READWRITE, /* 8 = WRITABLE */
213 PAGE_READWRITE, /* 9 = WRITABLE, SHARED */
214 PAGE_EXECUTE_READWRITE, /* 10 = WRITABLE, EXECUTABLE */
215 PAGE_EXECUTE_READWRITE, /* 11 = WRITABLE, EXECUTABLE, SHARED */
216 PAGE_READWRITE, /* 12 = WRITABLE, READABLE */
217 PAGE_READWRITE, /* 13 = WRITABLE, READABLE, SHARED */
218 PAGE_EXECUTE_READWRITE, /* 14 = WRITABLE, READABLE, EXECUTABLE */
219 PAGE_EXECUTE_READWRITE, /* 15 = WRITABLE, READABLE, EXECUTABLE, SHARED */
220 };
221
222 extern ACCESS_MASK MmMakeFileAccess[8];
223 static GENERIC_MAPPING MmpSectionMapping =
224 {
225 STANDARD_RIGHTS_READ | SECTION_MAP_READ | SECTION_QUERY,
226 STANDARD_RIGHTS_WRITE | SECTION_MAP_WRITE,
227 STANDARD_RIGHTS_EXECUTE | SECTION_MAP_EXECUTE,
228 SECTION_ALL_ACCESS
229 };
230
231
232 /* FUNCTIONS *****************************************************************/
233
234
235
236 NTSTATUS
237 NTAPI
MiWritePage(PMM_SECTION_SEGMENT Segment,LONGLONG SegOffset,PFN_NUMBER Page)238 MiWritePage(PMM_SECTION_SEGMENT Segment,
239 LONGLONG SegOffset,
240 PFN_NUMBER Page)
241 /*
242 * FUNCTION: write a page for a section backed memory area.
243 * PARAMETERS:
244 * MemoryArea - Memory area to write the page for.
245 * Offset - Offset of the page to write.
246 * Page - Page which contains the data to write.
247 */
248 {
249 NTSTATUS Status;
250 IO_STATUS_BLOCK IoStatus;
251 KEVENT Event;
252 UCHAR MdlBase[sizeof(MDL) + sizeof(PFN_NUMBER)];
253 PMDL Mdl = (PMDL)MdlBase;
254 PFILE_OBJECT FileObject = Segment->FileObject;
255 LARGE_INTEGER FileOffset;
256
257 FileOffset.QuadPart = Segment->Image.FileOffset + SegOffset;
258
259 RtlZeroMemory(MdlBase, sizeof(MdlBase));
260 MmInitializeMdl(Mdl, NULL, PAGE_SIZE);
261 MmBuildMdlFromPages(Mdl, &Page);
262 Mdl->MdlFlags |= MDL_PAGES_LOCKED;
263
264 KeInitializeEvent(&Event, NotificationEvent, FALSE);
265 Status = IoSynchronousPageWrite(FileObject, Mdl, &FileOffset, &Event, &IoStatus);
266 if (Status == STATUS_PENDING)
267 {
268 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
269 Status = IoStatus.Status;
270 }
271 if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
272 {
273 MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
274 }
275
276 return Status;
277 }
278
279
280 /*
281 References:
282 [1] Microsoft Corporation, "Microsoft Portable Executable and Common Object
283 File Format Specification", revision 6.0 (February 1999)
284 */
PeFmtCreateSection(IN CONST VOID * FileHeader,IN SIZE_T FileHeaderSize,IN PVOID File,OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,OUT PULONG Flags,IN PEXEFMT_CB_READ_FILE ReadFileCb,IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb)285 NTSTATUS NTAPI PeFmtCreateSection(IN CONST VOID * FileHeader,
286 IN SIZE_T FileHeaderSize,
287 IN PVOID File,
288 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
289 OUT PULONG Flags,
290 IN PEXEFMT_CB_READ_FILE ReadFileCb,
291 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb)
292 {
293 NTSTATUS nStatus;
294 ULONG cbFileHeaderOffsetSize = 0;
295 ULONG cbSectionHeadersOffset = 0;
296 ULONG cbSectionHeadersSize;
297 ULONG cbSectionHeadersOffsetSize = 0;
298 ULONG cbOptHeaderSize;
299 ULONG cbHeadersSize = 0;
300 ULONG nSectionAlignment;
301 ULONG nFileAlignment;
302 ULONG_PTR ImageBase = 0;
303 const IMAGE_DOS_HEADER * pidhDosHeader;
304 const IMAGE_NT_HEADERS32 * pinhNtHeader;
305 const IMAGE_OPTIONAL_HEADER32 * piohOptHeader;
306 const IMAGE_SECTION_HEADER * pishSectionHeaders;
307 PMM_SECTION_SEGMENT pssSegments;
308 LARGE_INTEGER lnOffset;
309 PVOID pBuffer;
310 SIZE_T nPrevVirtualEndOfSegment = 0;
311 ULONG nFileSizeOfHeaders = 0;
312 ULONG i;
313 ULONG AlignedLength;
314
315 ASSERT(FileHeader);
316 ASSERT(FileHeaderSize > 0);
317 ASSERT(File);
318 ASSERT(ImageSectionObject);
319 ASSERT(ReadFileCb);
320 ASSERT(AllocateSegmentsCb);
321
322 ASSERT(Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize));
323
324 ASSERT(((UINT_PTR)FileHeader % TYPE_ALIGNMENT(IMAGE_DOS_HEADER)) == 0);
325
326 #define DIE(ARGS_) { DPRINT ARGS_; goto l_Return; }
327
328 pBuffer = NULL;
329 pidhDosHeader = FileHeader;
330
331 /* DOS HEADER */
332 nStatus = STATUS_ROS_EXEFMT_UNKNOWN_FORMAT;
333
334 /* image too small to be an MZ executable */
335 if(FileHeaderSize < sizeof(IMAGE_DOS_HEADER))
336 DIE(("Too small to be an MZ executable, size is %lu\n", FileHeaderSize));
337
338 /* no MZ signature */
339 if(pidhDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
340 DIE(("No MZ signature found, e_magic is %hX\n", pidhDosHeader->e_magic));
341
342 /* NT HEADER */
343 nStatus = STATUS_INVALID_IMAGE_PROTECT;
344
345 /* not a Windows executable */
346 if(pidhDosHeader->e_lfanew <= 0)
347 DIE(("Not a Windows executable, e_lfanew is %d\n", pidhDosHeader->e_lfanew));
348
349 if(!Intsafe_AddULong32(&cbFileHeaderOffsetSize, pidhDosHeader->e_lfanew, RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader)))
350 DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
351
352 if(FileHeaderSize < cbFileHeaderOffsetSize)
353 pinhNtHeader = NULL;
354 else
355 {
356 /*
357 * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
358 * and FileHeaderSize >= cbFileHeaderOffsetSize, so this holds true too
359 */
360 ASSERT(Intsafe_CanOffsetPointer(FileHeader, pidhDosHeader->e_lfanew));
361 pinhNtHeader = (PVOID)((UINT_PTR)FileHeader + pidhDosHeader->e_lfanew);
362 }
363
364 /*
365 * the buffer doesn't contain the NT file header, or the alignment is wrong: we
366 * need to read the header from the file
367 */
368 if(FileHeaderSize < cbFileHeaderOffsetSize ||
369 (UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
370 {
371 ULONG cbNtHeaderSize;
372 ULONG cbReadSize;
373 PVOID pData;
374
375 l_ReadHeaderFromFile:
376 cbNtHeaderSize = 0;
377 lnOffset.QuadPart = pidhDosHeader->e_lfanew;
378
379 /* read the header from the file */
380 nStatus = ReadFileCb(File, &lnOffset, sizeof(IMAGE_NT_HEADERS64), &pData, &pBuffer, &cbReadSize);
381
382 if(!NT_SUCCESS(nStatus))
383 {
384 NTSTATUS ReturnedStatus = nStatus;
385
386 /* If it attempted to read past the end of the file, it means e_lfanew is invalid */
387 if (ReturnedStatus == STATUS_END_OF_FILE) nStatus = STATUS_INVALID_IMAGE_PROTECT;
388
389 DIE(("ReadFile failed, status %08X\n", ReturnedStatus));
390 }
391
392 ASSERT(pData);
393 ASSERT(pBuffer);
394 ASSERT(cbReadSize > 0);
395
396 nStatus = STATUS_INVALID_IMAGE_FORMAT;
397
398 /* the buffer doesn't contain the file header */
399 if(cbReadSize < RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader))
400 DIE(("The file doesn't contain the PE file header\n"));
401
402 pinhNtHeader = pData;
403
404 /* object still not aligned: copy it to the beginning of the buffer */
405 if((UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
406 {
407 ASSERT((UINT_PTR)pBuffer % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) == 0);
408 RtlMoveMemory(pBuffer, pData, cbReadSize);
409 pinhNtHeader = pBuffer;
410 }
411
412 /* invalid NT header */
413 nStatus = STATUS_INVALID_IMAGE_PROTECT;
414
415 if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
416 DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
417
418 nStatus = STATUS_INVALID_IMAGE_FORMAT;
419
420 if(!Intsafe_AddULong32(&cbNtHeaderSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
421 DIE(("The full NT header is too large\n"));
422
423 /* the buffer doesn't contain the whole NT header */
424 if(cbReadSize < cbNtHeaderSize)
425 DIE(("The file doesn't contain the full NT header\n"));
426 }
427 else
428 {
429 ULONG cbOptHeaderOffsetSize = 0;
430
431 nStatus = STATUS_INVALID_IMAGE_PROTECT;
432
433 /* don't trust an invalid NT header */
434 if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
435 DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
436
437 if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
438 DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
439
440 nStatus = STATUS_INVALID_IMAGE_FORMAT;
441
442 if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, cbOptHeaderOffsetSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
443 DIE(("The NT header is too large, SizeOfOptionalHeader is %X\n", pinhNtHeader->FileHeader.SizeOfOptionalHeader));
444
445 /* the buffer doesn't contain the whole NT header: read it from the file */
446 if(cbOptHeaderOffsetSize > FileHeaderSize)
447 goto l_ReadHeaderFromFile;
448 }
449
450 /* read information from the NT header */
451 piohOptHeader = &pinhNtHeader->OptionalHeader;
452 cbOptHeaderSize = pinhNtHeader->FileHeader.SizeOfOptionalHeader;
453
454 nStatus = STATUS_INVALID_IMAGE_FORMAT;
455
456 if(!RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Magic))
457 DIE(("The optional header doesn't contain the Magic field, SizeOfOptionalHeader is %X\n", cbOptHeaderSize));
458
459 /* ASSUME: RtlZeroMemory(ImageSectionObject, sizeof(*ImageSectionObject)); */
460
461 switch(piohOptHeader->Magic)
462 {
463 case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
464 #ifndef _WIN64
465 nStatus = STATUS_INVALID_IMAGE_WIN_64;
466 DIE(("Win64 optional header, unsupported\n"));
467 #else
468 // Fall through.
469 #endif
470 case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
471 break;
472 default:
473 DIE(("Unrecognized optional header, Magic is %X\n", piohOptHeader->Magic));
474 }
475
476 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SectionAlignment) &&
477 RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, FileAlignment))
478 {
479 /* See [1], section 3.4.2 */
480 if(piohOptHeader->SectionAlignment < PAGE_SIZE)
481 {
482 if(piohOptHeader->FileAlignment != piohOptHeader->SectionAlignment)
483 DIE(("Sections aren't page-aligned and the file alignment isn't the same\n"));
484 }
485 else if(piohOptHeader->SectionAlignment < piohOptHeader->FileAlignment)
486 DIE(("The section alignment is smaller than the file alignment\n"));
487
488 nSectionAlignment = piohOptHeader->SectionAlignment;
489 nFileAlignment = piohOptHeader->FileAlignment;
490
491 if(!IsPowerOf2(nSectionAlignment) || !IsPowerOf2(nFileAlignment))
492 DIE(("The section alignment (%u) and file alignment (%u) aren't both powers of 2\n", nSectionAlignment, nFileAlignment));
493 }
494 else
495 {
496 nSectionAlignment = PAGE_SIZE;
497 nFileAlignment = PAGE_SIZE;
498 }
499
500 ASSERT(IsPowerOf2(nSectionAlignment));
501 ASSERT(IsPowerOf2(nFileAlignment));
502
503 switch(piohOptHeader->Magic)
504 {
505 /* PE32 */
506 case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
507 {
508 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, ImageBase))
509 ImageBase = piohOptHeader->ImageBase;
510
511 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfImage))
512 ImageSectionObject->ImageInformation.ImageFileSize = piohOptHeader->SizeOfImage;
513
514 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackReserve))
515 ImageSectionObject->ImageInformation.MaximumStackSize = piohOptHeader->SizeOfStackReserve;
516
517 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackCommit))
518 ImageSectionObject->ImageInformation.CommittedStackSize = piohOptHeader->SizeOfStackCommit;
519
520 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Subsystem))
521 {
522 ImageSectionObject->ImageInformation.SubSystemType = piohOptHeader->Subsystem;
523
524 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MinorSubsystemVersion) &&
525 RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MajorSubsystemVersion))
526 {
527 ImageSectionObject->ImageInformation.SubSystemMinorVersion = piohOptHeader->MinorSubsystemVersion;
528 ImageSectionObject->ImageInformation.SubSystemMajorVersion = piohOptHeader->MajorSubsystemVersion;
529 }
530 }
531
532 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, AddressOfEntryPoint))
533 {
534 ImageSectionObject->ImageInformation.TransferAddress = (PVOID) (ImageBase +
535 piohOptHeader->AddressOfEntryPoint);
536 }
537
538 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfCode))
539 ImageSectionObject->ImageInformation.ImageContainsCode = piohOptHeader->SizeOfCode != 0;
540 else
541 ImageSectionObject->ImageInformation.ImageContainsCode = TRUE;
542
543 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, AddressOfEntryPoint))
544 {
545 if (piohOptHeader->AddressOfEntryPoint == 0)
546 {
547 ImageSectionObject->ImageInformation.ImageContainsCode = FALSE;
548 }
549 }
550
551 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, LoaderFlags))
552 ImageSectionObject->ImageInformation.LoaderFlags = piohOptHeader->LoaderFlags;
553
554 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, DllCharacteristics))
555 {
556 ImageSectionObject->ImageInformation.DllCharacteristics = piohOptHeader->DllCharacteristics;
557
558 /*
559 * Since we don't really implement SxS yet and LD doesn't supoprt /ALLOWISOLATION:NO, hard-code
560 * this flag here, which will prevent the loader and other code from doing any .manifest or SxS
561 * magic to any binary.
562 *
563 * This will break applications that depend on SxS when running with real Windows Kernel32/SxS/etc
564 * but honestly that's not tested. It will also break them when running no ReactOS once we implement
565 * the SxS support -- at which point, duh, this should be removed.
566 *
567 * But right now, any app depending on SxS is already broken anyway, so this flag only helps.
568 */
569 ImageSectionObject->ImageInformation.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_NO_ISOLATION;
570 }
571
572 break;
573 }
574 #ifdef _WIN64
575 /* PE64 */
576 case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
577 {
578 const IMAGE_OPTIONAL_HEADER64 * pioh64OptHeader;
579
580 pioh64OptHeader = (const IMAGE_OPTIONAL_HEADER64 *)piohOptHeader;
581
582 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, ImageBase))
583 {
584 ImageBase = pioh64OptHeader->ImageBase;
585 if(pioh64OptHeader->ImageBase > MAXULONG_PTR)
586 DIE(("ImageBase exceeds the address space\n"));
587 }
588
589 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfImage))
590 {
591 if(pioh64OptHeader->SizeOfImage > MAXULONG_PTR)
592 DIE(("SizeOfImage exceeds the address space\n"));
593
594 ImageSectionObject->ImageInformation.ImageFileSize = pioh64OptHeader->SizeOfImage;
595 }
596
597 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackReserve))
598 {
599 if(pioh64OptHeader->SizeOfStackReserve > MAXULONG_PTR)
600 DIE(("SizeOfStackReserve exceeds the address space\n"));
601
602 ImageSectionObject->ImageInformation.MaximumStackSize = (ULONG_PTR) pioh64OptHeader->SizeOfStackReserve;
603 }
604
605 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackCommit))
606 {
607 if(pioh64OptHeader->SizeOfStackCommit > MAXULONG_PTR)
608 DIE(("SizeOfStackCommit exceeds the address space\n"));
609
610 ImageSectionObject->ImageInformation.CommittedStackSize = (ULONG_PTR) pioh64OptHeader->SizeOfStackCommit;
611 }
612
613 if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, Subsystem))
614 {
615 ImageSectionObject->ImageInformation.SubSystemType = pioh64OptHeader->Subsystem;
616
617 if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, MinorSubsystemVersion) &&
618 RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, MajorSubsystemVersion))
619 {
620 ImageSectionObject->ImageInformation.SubSystemMinorVersion = pioh64OptHeader->MinorSubsystemVersion;
621 ImageSectionObject->ImageInformation.SubSystemMajorVersion = pioh64OptHeader->MajorSubsystemVersion;
622 }
623 }
624
625 if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, AddressOfEntryPoint))
626 {
627 ImageSectionObject->ImageInformation.TransferAddress = (PVOID) (ImageBase +
628 pioh64OptHeader->AddressOfEntryPoint);
629 }
630
631 if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfCode))
632 ImageSectionObject->ImageInformation.ImageContainsCode = pioh64OptHeader->SizeOfCode != 0;
633 else
634 ImageSectionObject->ImageInformation.ImageContainsCode = TRUE;
635
636 if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, AddressOfEntryPoint))
637 {
638 if (pioh64OptHeader->AddressOfEntryPoint == 0)
639 {
640 ImageSectionObject->ImageInformation.ImageContainsCode = FALSE;
641 }
642 }
643
644 if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, LoaderFlags))
645 ImageSectionObject->ImageInformation.LoaderFlags = pioh64OptHeader->LoaderFlags;
646
647 if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, DllCharacteristics))
648 ImageSectionObject->ImageInformation.DllCharacteristics = pioh64OptHeader->DllCharacteristics;
649
650 break;
651 }
652 #endif // _WIN64
653 }
654
655 /* [1], section 3.4.2 */
656 if((ULONG_PTR)ImageBase % 0x10000)
657 DIE(("ImageBase is not aligned on a 64KB boundary"));
658
659 ImageSectionObject->ImageInformation.ImageCharacteristics = pinhNtHeader->FileHeader.Characteristics;
660 ImageSectionObject->ImageInformation.Machine = pinhNtHeader->FileHeader.Machine;
661 ImageSectionObject->ImageInformation.GpValue = 0;
662 ImageSectionObject->ImageInformation.ZeroBits = 0;
663 ImageSectionObject->BasedAddress = (PVOID)ImageBase;
664
665 /* SECTION HEADERS */
666 nStatus = STATUS_INVALID_IMAGE_FORMAT;
667
668 /* see [1], section 3.3 */
669 if(pinhNtHeader->FileHeader.NumberOfSections > 96)
670 DIE(("Too many sections, NumberOfSections is %u\n", pinhNtHeader->FileHeader.NumberOfSections));
671
672 /*
673 * the additional segment is for the file's headers. They need to be present for
674 * the benefit of the dynamic loader (to locate exports, defaults for thread
675 * parameters, resources, etc.)
676 */
677 ImageSectionObject->NrSegments = pinhNtHeader->FileHeader.NumberOfSections + 1;
678
679 /* file offset for the section headers */
680 if(!Intsafe_AddULong32(&cbSectionHeadersOffset, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
681 DIE(("Offset overflow\n"));
682
683 if(!Intsafe_AddULong32(&cbSectionHeadersOffset, cbSectionHeadersOffset, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
684 DIE(("Offset overflow\n"));
685
686 /* size of the section headers */
687 ASSERT(Intsafe_CanMulULong32(pinhNtHeader->FileHeader.NumberOfSections, sizeof(IMAGE_SECTION_HEADER)));
688 cbSectionHeadersSize = pinhNtHeader->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
689
690 if(!Intsafe_AddULong32(&cbSectionHeadersOffsetSize, cbSectionHeadersOffset, cbSectionHeadersSize))
691 DIE(("Section headers too large\n"));
692
693 /* size of the executable's headers */
694 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfHeaders))
695 {
696 // if(!IsAligned(piohOptHeader->SizeOfHeaders, nFileAlignment))
697 // DIE(("SizeOfHeaders is not aligned\n"));
698
699 if(cbSectionHeadersSize > piohOptHeader->SizeOfHeaders)
700 DIE(("The section headers overflow SizeOfHeaders\n"));
701
702 cbHeadersSize = piohOptHeader->SizeOfHeaders;
703 }
704 else if(!AlignUp(&cbHeadersSize, cbSectionHeadersOffsetSize, nFileAlignment))
705 DIE(("Overflow aligning the size of headers\n"));
706
707 if(pBuffer)
708 {
709 ExFreePool(pBuffer);
710 pBuffer = NULL;
711 }
712 /* WARNING: pinhNtHeader IS NO LONGER USABLE */
713 /* WARNING: piohOptHeader IS NO LONGER USABLE */
714 /* WARNING: pioh64OptHeader IS NO LONGER USABLE */
715
716 if(FileHeaderSize < cbSectionHeadersOffsetSize)
717 pishSectionHeaders = NULL;
718 else
719 {
720 /*
721 * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
722 * and FileHeaderSize >= cbSectionHeadersOffsetSize, so this holds true too
723 */
724 ASSERT(Intsafe_CanOffsetPointer(FileHeader, cbSectionHeadersOffset));
725 pishSectionHeaders = (PVOID)((UINT_PTR)FileHeader + cbSectionHeadersOffset);
726 }
727
728 /*
729 * the buffer doesn't contain the section headers, or the alignment is wrong:
730 * read the headers from the file
731 */
732 if(FileHeaderSize < cbSectionHeadersOffsetSize ||
733 (UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0)
734 {
735 PVOID pData;
736 ULONG cbReadSize;
737
738 lnOffset.QuadPart = cbSectionHeadersOffset;
739
740 /* read the header from the file */
741 nStatus = ReadFileCb(File, &lnOffset, cbSectionHeadersSize, &pData, &pBuffer, &cbReadSize);
742
743 if(!NT_SUCCESS(nStatus))
744 DIE(("ReadFile failed with status %08X\n", nStatus));
745
746 ASSERT(pData);
747 ASSERT(pBuffer);
748 ASSERT(cbReadSize > 0);
749
750 nStatus = STATUS_INVALID_IMAGE_FORMAT;
751
752 /* the buffer doesn't contain all the section headers */
753 if(cbReadSize < cbSectionHeadersSize)
754 DIE(("The file doesn't contain all of the section headers\n"));
755
756 pishSectionHeaders = pData;
757
758 /* object still not aligned: copy it to the beginning of the buffer */
759 if((UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0)
760 {
761 ASSERT((UINT_PTR)pBuffer % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) == 0);
762 RtlMoveMemory(pBuffer, pData, cbReadSize);
763 pishSectionHeaders = pBuffer;
764 }
765 }
766
767 /* SEGMENTS */
768 /* allocate the segments */
769 nStatus = STATUS_INSUFFICIENT_RESOURCES;
770 ImageSectionObject->Segments = AllocateSegmentsCb(ImageSectionObject->NrSegments);
771
772 if(ImageSectionObject->Segments == NULL)
773 DIE(("AllocateSegments failed\n"));
774
775 /* initialize the headers segment */
776 pssSegments = ImageSectionObject->Segments;
777
778 // ASSERT(IsAligned(cbHeadersSize, nFileAlignment));
779
780 if(!AlignUp(&nFileSizeOfHeaders, cbHeadersSize, nFileAlignment))
781 DIE(("Cannot align the size of the section headers\n"));
782
783 nPrevVirtualEndOfSegment = ALIGN_UP_BY(cbHeadersSize, nSectionAlignment);
784 if (nPrevVirtualEndOfSegment < cbHeadersSize)
785 DIE(("Cannot align the size of the section headers\n"));
786
787 pssSegments[0].Image.FileOffset = 0;
788 pssSegments[0].Protection = PAGE_READONLY;
789 pssSegments[0].Length.QuadPart = nPrevVirtualEndOfSegment;
790 pssSegments[0].RawLength.QuadPart = nFileSizeOfHeaders;
791 pssSegments[0].Image.VirtualAddress = 0;
792 pssSegments[0].Image.Characteristics = 0;
793 pssSegments[0].WriteCopy = TRUE;
794
795 /* skip the headers segment */
796 ++ pssSegments;
797
798 nStatus = STATUS_INVALID_IMAGE_FORMAT;
799
800 ASSERT(ImageSectionObject->RefCount > 0);
801
802 /* convert the executable sections into segments. See also [1], section 4 */
803 for(i = 0; i < ImageSectionObject->NrSegments - 1; ++ i)
804 {
805 ULONG nCharacteristics;
806
807 /* validate the alignment */
808 if(!IsAligned(pishSectionHeaders[i].VirtualAddress, nSectionAlignment))
809 DIE(("Image.VirtualAddress[%u] is not aligned\n", i));
810
811 /* sections must be contiguous, ordered by base address and non-overlapping */
812 if(pishSectionHeaders[i].VirtualAddress != nPrevVirtualEndOfSegment)
813 DIE(("Memory gap between section %u and the previous\n", i));
814
815 /* ignore explicit BSS sections */
816 if(pishSectionHeaders[i].PointerToRawData != 0 && pishSectionHeaders[i].SizeOfRawData != 0)
817 {
818 /* validate the alignment */
819 #if 0
820 /* Yes, this should be a multiple of FileAlignment, but there's
821 * stuff out there that isn't. We can cope with that
822 */
823 if(!IsAligned(pishSectionHeaders[i].SizeOfRawData, nFileAlignment))
824 DIE(("SizeOfRawData[%u] is not aligned\n", i));
825 #endif
826
827 // if(!IsAligned(pishSectionHeaders[i].PointerToRawData, nFileAlignment))
828 // DIE(("PointerToRawData[%u] is not aligned\n", i));
829
830 if(!Intsafe_CanAddULong32(pishSectionHeaders[i].PointerToRawData, pishSectionHeaders[i].SizeOfRawData))
831 DIE(("SizeOfRawData[%u] too large\n", i));
832
833 /* conversion */
834 pssSegments[i].Image.FileOffset = pishSectionHeaders[i].PointerToRawData;
835 pssSegments[i].RawLength.QuadPart = pishSectionHeaders[i].SizeOfRawData;
836 }
837 else
838 {
839 /* FIXME: Should reset PointerToRawData to 0 in the image mapping */
840 ASSERT(pssSegments[i].Image.FileOffset == 0);
841 ASSERT(pssSegments[i].RawLength.QuadPart == 0);
842 }
843
844 ASSERT(Intsafe_CanAddLong64(pssSegments[i].Image.FileOffset, pssSegments[i].RawLength.QuadPart));
845
846 nCharacteristics = pishSectionHeaders[i].Characteristics;
847
848 /* no explicit protection */
849 if((nCharacteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)) == 0)
850 {
851 if(nCharacteristics & IMAGE_SCN_CNT_CODE)
852 nCharacteristics |= IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ;
853
854 if(nCharacteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
855 nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
856
857 if(nCharacteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
858 nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
859 }
860
861 /* see table above */
862 pssSegments[i].Protection = SectionCharacteristicsToProtect[nCharacteristics >> 28];
863 pssSegments[i].WriteCopy = !(nCharacteristics & IMAGE_SCN_MEM_SHARED);
864
865 if(pishSectionHeaders[i].Misc.VirtualSize == 0)
866 pssSegments[i].Length.QuadPart = pishSectionHeaders[i].SizeOfRawData;
867 else
868 pssSegments[i].Length.QuadPart = pishSectionHeaders[i].Misc.VirtualSize;
869
870 AlignedLength = ALIGN_UP_BY(pssSegments[i].Length.LowPart, nSectionAlignment);
871 if(AlignedLength < pssSegments[i].Length.LowPart)
872 DIE(("Cannot align the virtual size of section %u\n", i));
873
874 pssSegments[i].Length.LowPart = AlignedLength;
875
876 if(pssSegments[i].Length.QuadPart == 0)
877 DIE(("Virtual size of section %u is null\n", i));
878
879 pssSegments[i].Image.VirtualAddress = pishSectionHeaders[i].VirtualAddress;
880 pssSegments[i].Image.Characteristics = pishSectionHeaders[i].Characteristics;
881
882 /* ensure the memory image is no larger than 4GB */
883 nPrevVirtualEndOfSegment = (ULONG_PTR)(pssSegments[i].Image.VirtualAddress + pssSegments[i].Length.QuadPart);
884 if (nPrevVirtualEndOfSegment < pssSegments[i].Image.VirtualAddress)
885 DIE(("The image is too large\n"));
886 }
887
888 if(nSectionAlignment >= PAGE_SIZE)
889 *Flags |= EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED;
890
891 /* Success */
892 nStatus = STATUS_SUCCESS;// STATUS_ROS_EXEFMT_LOADED_FORMAT | EXEFMT_LOADED_PE32;
893
894 l_Return:
895 if(pBuffer)
896 ExFreePool(pBuffer);
897
898 return nStatus;
899 }
900
901 /*
902 * FUNCTION: Waits in kernel mode indefinitely for a file object lock.
903 * ARGUMENTS: PFILE_OBJECT to wait for.
904 * RETURNS: Status of the wait.
905 */
906 NTSTATUS
MmspWaitForFileLock(PFILE_OBJECT File)907 MmspWaitForFileLock(PFILE_OBJECT File)
908 {
909 return STATUS_SUCCESS;
910 //return KeWaitForSingleObject(&File->Lock, 0, KernelMode, FALSE, NULL);
911 }
912
913
914
915 VOID
916 NTAPI
MmpFreePageFileSegment(PMM_SECTION_SEGMENT Segment)917 MmpFreePageFileSegment(PMM_SECTION_SEGMENT Segment)
918 {
919 ULONG Length;
920 LARGE_INTEGER Offset;
921 ULONG_PTR Entry;
922 SWAPENTRY SavedSwapEntry;
923 PFN_NUMBER Page;
924
925 Page = 0;
926
927 MmLockSectionSegment(Segment);
928
929 Length = PAGE_ROUND_UP(Segment->Length.QuadPart);
930 for (Offset.QuadPart = 0; Offset.QuadPart < Length; Offset.QuadPart += PAGE_SIZE)
931 {
932 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
933 if (Entry)
934 {
935 MmSetPageEntrySectionSegment(Segment, &Offset, 0);
936 if (IS_SWAP_FROM_SSE(Entry))
937 {
938 MmFreeSwapPage(SWAPENTRY_FROM_SSE(Entry));
939 }
940 else
941 {
942 Page = PFN_FROM_SSE(Entry);
943 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
944 if (SavedSwapEntry != 0)
945 {
946 MmSetSavedSwapEntryPage(Page, 0);
947 MmFreeSwapPage(SavedSwapEntry);
948 }
949 MmReleasePageMemoryConsumer(MC_USER, Page);
950 }
951 }
952 }
953
954 MmUnlockSectionSegment(Segment);
955 }
956
957 static
958 VOID
959 NTAPI
FreeSegmentPage(PMM_SECTION_SEGMENT Segment,PLARGE_INTEGER Offset)960 FreeSegmentPage(PMM_SECTION_SEGMENT Segment, PLARGE_INTEGER Offset)
961 {
962 ULONG_PTR Entry;
963 PFN_NUMBER Page;
964
965 MmLockSectionSegment(Segment);
966
967 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
968
969 MmUnlockSectionSegment(Segment);
970
971 /* This must be either a valid entry or nothing */
972 ASSERT(!IS_SWAP_FROM_SSE(Entry));
973
974 /* There should be no reference anymore */
975 ASSERT(SHARE_COUNT_FROM_SSE(Entry) == 0);
976
977 Page = PFN_FROM_SSE(Entry);
978 /* If there is a page, this must be because it's still dirty */
979 ASSERT(Page != 0);
980
981 /* Write the page */
982 if (IS_DIRTY_SSE(Entry))
983 MiWritePage(Segment, Offset->QuadPart, Page);
984
985 MmReleasePageMemoryConsumer(MC_USER, Page);
986 }
987
988 _When_(OldIrql == MM_NOIRQL, _IRQL_requires_max_(DISPATCH_LEVEL))
989 _When_(OldIrql == MM_NOIRQL, _Requires_lock_not_held_(MmPfnLock))
990 _When_(OldIrql != MM_NOIRQL, _Requires_lock_held_(MmPfnLock))
991 _When_(OldIrql != MM_NOIRQL, _Releases_lock_(MmPfnLock))
992 _When_(OldIrql != MM_NOIRQL, _IRQL_requires_(DISPATCH_LEVEL))
993 VOID
994 NTAPI
995 MmDereferenceSegmentWithLock(
996 _Inout_ PMM_SECTION_SEGMENT Segment,
997 _In_ _When_(OldIrql != MM_NOIRQL, _IRQL_restores_) KIRQL OldIrql)
998 {
999 /* Lock the PFN lock because we mess around with SectionObjectPointers */
1000 if (OldIrql == MM_NOIRQL)
1001 {
1002 OldIrql = MiAcquirePfnLock();
1003 }
1004
1005 if (InterlockedDecrement64(Segment->ReferenceCount) > 0)
1006 {
1007 /* Nothing to do yet */
1008 MiReleasePfnLock(OldIrql);
1009 return;
1010 }
1011
1012 *Segment->Flags |= MM_SEGMENT_INDELETE;
1013
1014 /* Flush the segment */
1015 if (*Segment->Flags & MM_DATAFILE_SEGMENT)
1016 {
1017 MiReleasePfnLock(OldIrql);
1018 /* Free the page table. This will flush any remaining dirty data */
1019 MmFreePageTablesSectionSegment(Segment, FreeSegmentPage);
1020
1021 OldIrql = MiAcquirePfnLock();
1022 /* Delete the pointer on the file */
1023 ASSERT(Segment->FileObject->SectionObjectPointer->DataSectionObject == Segment);
1024 Segment->FileObject->SectionObjectPointer->DataSectionObject = NULL;
1025 MiReleasePfnLock(OldIrql);
1026 ObDereferenceObject(Segment->FileObject);
1027
1028 ExFreePoolWithTag(Segment, TAG_MM_SECTION_SEGMENT);
1029 }
1030 else
1031 {
1032 /* Most grotesque thing ever */
1033 PMM_IMAGE_SECTION_OBJECT ImageSectionObject = CONTAINING_RECORD(Segment->ReferenceCount, MM_IMAGE_SECTION_OBJECT, RefCount);
1034 PMM_SECTION_SEGMENT SectionSegments;
1035 ULONG NrSegments;
1036 ULONG i;
1037
1038 /* Delete the pointer on the file */
1039 ASSERT(ImageSectionObject->FileObject->SectionObjectPointer->ImageSectionObject == ImageSectionObject);
1040 ImageSectionObject->FileObject->SectionObjectPointer->ImageSectionObject = NULL;
1041 MiReleasePfnLock(OldIrql);
1042
1043 ObDereferenceObject(ImageSectionObject->FileObject);
1044
1045 NrSegments = ImageSectionObject->NrSegments;
1046 SectionSegments = ImageSectionObject->Segments;
1047 for (i = 0; i < NrSegments; i++)
1048 {
1049 if (SectionSegments[i].Image.Characteristics & IMAGE_SCN_MEM_SHARED)
1050 {
1051 MmpFreePageFileSegment(&SectionSegments[i]);
1052 }
1053
1054 MmFreePageTablesSectionSegment(&SectionSegments[i], NULL);
1055 }
1056
1057 ExFreePoolWithTag(ImageSectionObject->Segments, TAG_MM_SECTION_SEGMENT);
1058 ExFreePoolWithTag(ImageSectionObject, TAG_MM_SECTION_SEGMENT);
1059 }
1060 }
1061
1062 VOID
1063 NTAPI
MmSharePageEntrySectionSegment(PMM_SECTION_SEGMENT Segment,PLARGE_INTEGER Offset)1064 MmSharePageEntrySectionSegment(PMM_SECTION_SEGMENT Segment,
1065 PLARGE_INTEGER Offset)
1066 {
1067 ULONG_PTR Entry;
1068
1069 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
1070 if (Entry == 0)
1071 {
1072 DPRINT1("Entry == 0 for MmSharePageEntrySectionSegment\n");
1073 KeBugCheck(MEMORY_MANAGEMENT);
1074 }
1075 if (SHARE_COUNT_FROM_SSE(Entry) == MAX_SHARE_COUNT)
1076 {
1077 DPRINT1("Maximum share count reached\n");
1078 KeBugCheck(MEMORY_MANAGEMENT);
1079 }
1080 if (IS_SWAP_FROM_SSE(Entry))
1081 {
1082 KeBugCheck(MEMORY_MANAGEMENT);
1083 }
1084 MmSetPageEntrySectionSegment(Segment, Offset, BUMPREF_SSE(Entry));
1085 }
1086
1087 BOOLEAN
1088 NTAPI
MmUnsharePageEntrySectionSegment(PMEMORY_AREA MemoryArea,PMM_SECTION_SEGMENT Segment,PLARGE_INTEGER Offset,BOOLEAN Dirty,BOOLEAN PageOut,ULONG_PTR * InEntry)1089 MmUnsharePageEntrySectionSegment(PMEMORY_AREA MemoryArea,
1090 PMM_SECTION_SEGMENT Segment,
1091 PLARGE_INTEGER Offset,
1092 BOOLEAN Dirty,
1093 BOOLEAN PageOut,
1094 ULONG_PTR *InEntry)
1095 {
1096 ULONG_PTR Entry = InEntry ? *InEntry : MmGetPageEntrySectionSegment(Segment, Offset);
1097 PFN_NUMBER Page = PFN_FROM_SSE(Entry);
1098 BOOLEAN IsDataMap = BooleanFlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT);
1099 SWAPENTRY SwapEntry;
1100
1101 if (Entry == 0)
1102 {
1103 DPRINT1("Entry == 0 for MmUnsharePageEntrySectionSegment\n");
1104 KeBugCheck(MEMORY_MANAGEMENT);
1105 }
1106 if (SHARE_COUNT_FROM_SSE(Entry) == 0)
1107 {
1108 DPRINT1("Zero share count for unshare (Seg %p Offset %x Page %x)\n", Segment, Offset->LowPart, PFN_FROM_SSE(Entry));
1109 KeBugCheck(MEMORY_MANAGEMENT);
1110 }
1111 if (IS_SWAP_FROM_SSE(Entry))
1112 {
1113 KeBugCheck(MEMORY_MANAGEMENT);
1114 }
1115 Entry = DECREF_SSE(Entry);
1116 if (Dirty) Entry = DIRTY_SSE(Entry);
1117
1118 /* If we are paging-out, pruning the page for real will be taken care of in MmCheckDirtySegment */
1119 if ((SHARE_COUNT_FROM_SSE(Entry) > 0) || PageOut)
1120 {
1121 /* Update the page mapping in the segment and we're done */
1122 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
1123 return FALSE;
1124 }
1125
1126 /* We are pruning the last mapping on this page. See if we can keep it a bit more. */
1127 ASSERT(!PageOut);
1128
1129 if (IsDataMap)
1130 {
1131 /* We can always keep memory in for data maps */
1132 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
1133 return FALSE;
1134 }
1135
1136 if (!FlagOn(Segment->Image.Characteristics, IMAGE_SCN_MEM_SHARED))
1137 {
1138 ASSERT(Segment->WriteCopy);
1139 ASSERT(!IS_DIRTY_SSE(Entry));
1140 ASSERT(MmGetSavedSwapEntryPage(Page) == 0);
1141 /* So this must have been a read-only page. Keep it ! */
1142 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
1143 return FALSE;
1144 }
1145
1146 /*
1147 * So this is a page for a shared section of a DLL.
1148 * We can keep it if it is not dirty.
1149 */
1150 SwapEntry = MmGetSavedSwapEntryPage(Page);
1151 if ((SwapEntry == 0) && !IS_DIRTY_SSE(Entry))
1152 {
1153 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
1154 return FALSE;
1155 }
1156
1157 /* No more processes are referencing this shared dirty page. Ditch it. */
1158 if (SwapEntry)
1159 {
1160 MmSetSavedSwapEntryPage(Page, 0);
1161 MmFreeSwapPage(SwapEntry);
1162 }
1163 MmSetPageEntrySectionSegment(Segment, Offset, 0);
1164 MmReleasePageMemoryConsumer(MC_USER, Page);
1165 return TRUE;
1166 }
1167
1168 static
1169 NTSTATUS
MiCopyFromUserPage(PFN_NUMBER DestPage,const VOID * SrcAddress)1170 MiCopyFromUserPage(PFN_NUMBER DestPage, const VOID *SrcAddress)
1171 {
1172 PEPROCESS Process;
1173 KIRQL Irql;
1174 PVOID DestAddress;
1175
1176 Process = PsGetCurrentProcess();
1177 DestAddress = MiMapPageInHyperSpace(Process, DestPage, &Irql);
1178 if (DestAddress == NULL)
1179 {
1180 return STATUS_NO_MEMORY;
1181 }
1182 ASSERT((ULONG_PTR)DestAddress % PAGE_SIZE == 0);
1183 ASSERT((ULONG_PTR)SrcAddress % PAGE_SIZE == 0);
1184 RtlCopyMemory(DestAddress, SrcAddress, PAGE_SIZE);
1185 MiUnmapPageInHyperSpace(Process, DestAddress, Irql);
1186 return STATUS_SUCCESS;
1187 }
1188
1189 static
1190 NTSTATUS
1191 NTAPI
MmMakeSegmentResident(_In_ PMM_SECTION_SEGMENT Segment,_In_ LONGLONG Offset,_In_ ULONG Length,_In_opt_ PLARGE_INTEGER ValidDataLength,_In_ BOOLEAN SetDirty)1192 MmMakeSegmentResident(
1193 _In_ PMM_SECTION_SEGMENT Segment,
1194 _In_ LONGLONG Offset,
1195 _In_ ULONG Length,
1196 _In_opt_ PLARGE_INTEGER ValidDataLength,
1197 _In_ BOOLEAN SetDirty)
1198 {
1199 /* Let's use a 64K granularity. */
1200 LONGLONG RangeStart, RangeEnd;
1201 NTSTATUS Status;
1202 PFILE_OBJECT FileObject = Segment->FileObject;
1203
1204 /* Calculate our range, aligned on 64K if possible. */
1205 Status = RtlLongLongAdd(Offset, Length, &RangeEnd);
1206 ASSERT(NT_SUCCESS(Status));
1207 if (!NT_SUCCESS(Status))
1208 return Status;
1209
1210 /* If the file is not random access and we are not the page out thread
1211 * read a 64K Chunk. */
1212 if (((ULONG_PTR)IoGetTopLevelIrp() != FSRTL_MOD_WRITE_TOP_LEVEL_IRP)
1213 && !FlagOn(FileObject->Flags, FO_RANDOM_ACCESS))
1214 {
1215 RangeStart = Offset - (Offset % _64K);
1216 if (RangeEnd % _64K)
1217 RangeEnd += _64K - (RangeEnd % _64K);
1218 }
1219 else
1220 {
1221 RangeStart = Offset - (Offset % PAGE_SIZE);
1222 if (RangeEnd % PAGE_SIZE)
1223 RangeEnd += PAGE_SIZE - (RangeEnd % PAGE_SIZE);
1224 }
1225
1226 /* Clamp if needed */
1227 if (!FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT))
1228 {
1229 if (RangeEnd > Segment->RawLength.QuadPart)
1230 RangeEnd = Segment->RawLength.QuadPart;
1231 }
1232
1233 /* Let's gooooooooo */
1234 for ( ; RangeStart < RangeEnd; RangeStart += _64K)
1235 {
1236 /* First take a look at where we miss pages */
1237 ULONG ToReadPageBits = 0;
1238 LONGLONG ChunkEnd = RangeStart + _64K;
1239
1240 if (ChunkEnd > RangeEnd)
1241 ChunkEnd = RangeEnd;
1242
1243 MmLockSectionSegment(Segment);
1244 for (LONGLONG ChunkOffset = RangeStart; ChunkOffset < ChunkEnd; ChunkOffset += PAGE_SIZE)
1245 {
1246 LARGE_INTEGER CurrentOffset;
1247
1248 CurrentOffset.QuadPart = ChunkOffset;
1249 ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &CurrentOffset);
1250
1251 /* Let any pending read proceed */
1252 while (MM_IS_WAIT_PTE(Entry))
1253 {
1254 MmUnlockSectionSegment(Segment);
1255
1256 KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
1257
1258 MmLockSectionSegment(Segment);
1259 Entry = MmGetPageEntrySectionSegment(Segment, &CurrentOffset);
1260 }
1261
1262 if (Entry != 0)
1263 {
1264 /* Dirtify it if it's a resident page and we're asked to */
1265 if (SetDirty && !IS_SWAP_FROM_SSE(Entry))
1266 MmSetPageEntrySectionSegment(Segment, &CurrentOffset, DIRTY_SSE(Entry));
1267 continue;
1268 }
1269
1270 ToReadPageBits |= 1UL << ((ChunkOffset - RangeStart) >> PAGE_SHIFT);
1271
1272 /* Put a wait entry here */
1273 MmSetPageEntrySectionSegment(Segment, &CurrentOffset, MAKE_SWAP_SSE(MM_WAIT_ENTRY));
1274 }
1275 MmUnlockSectionSegment(Segment);
1276
1277 if (ToReadPageBits == 0)
1278 {
1279 /* Nothing to do for this chunk */
1280 continue;
1281 }
1282
1283 /* Now perform the actual read */
1284 LONGLONG ChunkOffset = RangeStart;
1285 while (ChunkOffset < ChunkEnd)
1286 {
1287 /* Move forward if there is a hole */
1288 ULONG BitSet;
1289 if (!_BitScanForward(&BitSet, ToReadPageBits))
1290 {
1291 /* Nothing more to read */
1292 break;
1293 }
1294 ToReadPageBits >>= BitSet;
1295 ChunkOffset += BitSet * PAGE_SIZE;
1296 ASSERT(ChunkOffset < ChunkEnd);
1297
1298 /* Get the range we have to read */
1299 _BitScanForward(&BitSet, ~ToReadPageBits);
1300 ULONG ReadLength = BitSet * PAGE_SIZE;
1301
1302 ASSERT(ReadLength <= _64K);
1303
1304 /* Clamp (This is for image mappings */
1305 if ((ChunkOffset + ReadLength) > ChunkEnd)
1306 ReadLength = ChunkEnd - ChunkOffset;
1307
1308 ASSERT(ReadLength != 0);
1309
1310 /* Allocate a MDL */
1311 PMDL Mdl = IoAllocateMdl(NULL, ReadLength, FALSE, FALSE, NULL);
1312 if (!Mdl)
1313 {
1314 /* Damn. Roll-back. */
1315 MmLockSectionSegment(Segment);
1316 while (ChunkOffset < ChunkEnd)
1317 {
1318 if (ToReadPageBits & 1)
1319 {
1320 LARGE_INTEGER CurrentOffset;
1321 CurrentOffset.QuadPart = ChunkOffset;
1322 ASSERT(MM_IS_WAIT_PTE(MmGetPageEntrySectionSegment(Segment, &CurrentOffset)));
1323 MmSetPageEntrySectionSegment(Segment, &CurrentOffset, 0);
1324 }
1325 ToReadPageBits >>= 1;
1326 ChunkOffset += PAGE_SIZE;
1327 }
1328 MmUnlockSectionSegment(Segment);
1329 return STATUS_INSUFFICIENT_RESOURCES;
1330 }
1331
1332 /* Get our pages */
1333 PPFN_NUMBER Pages = MmGetMdlPfnArray(Mdl);
1334 RtlZeroMemory(Pages, BYTES_TO_PAGES(ReadLength) * sizeof(PFN_NUMBER));
1335 for (UINT i = 0; i < BYTES_TO_PAGES(ReadLength); i++)
1336 {
1337 Status = MmRequestPageMemoryConsumer(MC_USER, FALSE, &Pages[i]);
1338 if (!NT_SUCCESS(Status))
1339 {
1340 /* Damn. Roll-back. */
1341 for (UINT j = 0; j < i; j++)
1342 MmReleasePageMemoryConsumer(MC_USER, Pages[j]);
1343 goto Failed;
1344 }
1345 }
1346
1347 Mdl->MdlFlags |= MDL_PAGES_LOCKED | MDL_IO_PAGE_READ;
1348
1349 LARGE_INTEGER FileOffset;
1350 FileOffset.QuadPart = Segment->Image.FileOffset + ChunkOffset;
1351
1352 /* Clamp to VDL */
1353 if (ValidDataLength && ((FileOffset.QuadPart + ReadLength) > ValidDataLength->QuadPart))
1354 {
1355 if (FileOffset.QuadPart > ValidDataLength->QuadPart)
1356 {
1357 /* Great, nothing to read. */
1358 goto AssignPagesToSegment;
1359 }
1360
1361 Mdl->Size = (FileOffset.QuadPart + ReadLength) - ValidDataLength->QuadPart;
1362 }
1363
1364 KEVENT Event;
1365 KeInitializeEvent(&Event, NotificationEvent, FALSE);
1366
1367 /* Disable APCs */
1368 KIRQL OldIrql;
1369 KeRaiseIrql(APC_LEVEL, &OldIrql);
1370
1371 IO_STATUS_BLOCK Iosb;
1372 Status = IoPageRead(FileObject, Mdl, &FileOffset, &Event, &Iosb);
1373 if (Status == STATUS_PENDING)
1374 {
1375 KeWaitForSingleObject(&Event, WrPageIn, KernelMode, FALSE, NULL);
1376 Status = Iosb.Status;
1377 }
1378
1379 if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
1380 {
1381 MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
1382 }
1383
1384 KeLowerIrql(OldIrql);
1385
1386 if (Status == STATUS_END_OF_FILE)
1387 {
1388 DPRINT1("Got STATUS_END_OF_FILE at offset %I64d for file %wZ.\n", FileOffset.QuadPart, &FileObject->FileName);
1389 Status = STATUS_SUCCESS;
1390 }
1391
1392 if (!NT_SUCCESS(Status))
1393 {
1394 /* Damn. Roll back. */
1395 for (UINT i = 0; i < BYTES_TO_PAGES(ReadLength); i++)
1396 MmReleasePageMemoryConsumer(MC_USER, Pages[i]);
1397
1398 Failed:
1399 MmLockSectionSegment(Segment);
1400 while (ChunkOffset < ChunkEnd)
1401 {
1402 if (ToReadPageBits & 1)
1403 {
1404 LARGE_INTEGER CurrentOffset;
1405 CurrentOffset.QuadPart = ChunkOffset;
1406 ASSERT(MM_IS_WAIT_PTE(MmGetPageEntrySectionSegment(Segment, &CurrentOffset)));
1407 MmSetPageEntrySectionSegment(Segment, &CurrentOffset, 0);
1408 }
1409 ToReadPageBits >>= 1;
1410 ChunkOffset += PAGE_SIZE;
1411 }
1412 MmUnlockSectionSegment(Segment);
1413 IoFreeMdl(Mdl);;
1414 return Status;
1415 }
1416
1417 AssignPagesToSegment:
1418 MmLockSectionSegment(Segment);
1419
1420 for (UINT i = 0; i < BYTES_TO_PAGES(ReadLength); i++)
1421 {
1422 ULONG_PTR Entry = MAKE_SSE(Pages[i] << PAGE_SHIFT, 0);
1423 LARGE_INTEGER CurrentOffset;
1424 CurrentOffset.QuadPart = ChunkOffset + (i * PAGE_SIZE);
1425
1426 ASSERT(MM_IS_WAIT_PTE(MmGetPageEntrySectionSegment(Segment, &CurrentOffset)));
1427
1428 if (SetDirty)
1429 Entry = DIRTY_SSE(Entry);
1430
1431 MmSetPageEntrySectionSegment(Segment, &CurrentOffset, Entry);
1432 }
1433
1434 MmUnlockSectionSegment(Segment);
1435
1436 IoFreeMdl(Mdl);
1437 ToReadPageBits >>= BitSet;
1438 ChunkOffset += BitSet * PAGE_SIZE;
1439 }
1440 }
1441
1442 return STATUS_SUCCESS;
1443 }
1444
1445 static VOID
MmAlterViewAttributes(PMMSUPPORT AddressSpace,PVOID BaseAddress,SIZE_T RegionSize,ULONG OldType,ULONG OldProtect,ULONG NewType,ULONG NewProtect)1446 MmAlterViewAttributes(PMMSUPPORT AddressSpace,
1447 PVOID BaseAddress,
1448 SIZE_T RegionSize,
1449 ULONG OldType,
1450 ULONG OldProtect,
1451 ULONG NewType,
1452 ULONG NewProtect)
1453 {
1454 PMEMORY_AREA MemoryArea;
1455 PMM_SECTION_SEGMENT Segment;
1456 BOOLEAN DoCOW = FALSE;
1457 ULONG i;
1458 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
1459
1460 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
1461 ASSERT(MemoryArea != NULL);
1462 Segment = MemoryArea->SectionData.Segment;
1463 MmLockSectionSegment(Segment);
1464
1465 if ((Segment->WriteCopy) &&
1466 (NewProtect == PAGE_READWRITE || NewProtect == PAGE_EXECUTE_READWRITE))
1467 {
1468 DoCOW = TRUE;
1469 }
1470
1471 if (OldProtect != NewProtect)
1472 {
1473 for (i = 0; i < PAGE_ROUND_UP(RegionSize) / PAGE_SIZE; i++)
1474 {
1475 SWAPENTRY SwapEntry;
1476 PVOID Address = (char*)BaseAddress + (i * PAGE_SIZE);
1477 ULONG Protect = NewProtect;
1478
1479 /* Wait for a wait entry to disappear */
1480 do
1481 {
1482 MmGetPageFileMapping(Process, Address, &SwapEntry);
1483 if (SwapEntry != MM_WAIT_ENTRY)
1484 break;
1485 MmUnlockSectionSegment(Segment);
1486 MmUnlockAddressSpace(AddressSpace);
1487 KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
1488 MmLockAddressSpace(AddressSpace);
1489 MmLockSectionSegment(Segment);
1490 }
1491 while (TRUE);
1492
1493 /*
1494 * If we doing COW for this segment then check if the page is
1495 * already private.
1496 */
1497 if (DoCOW && (MmIsPagePresent(Process, Address) || MmIsDisabledPage(Process, Address)))
1498 {
1499 LARGE_INTEGER Offset;
1500 ULONG_PTR Entry;
1501 PFN_NUMBER Page;
1502
1503 Offset.QuadPart = (ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea)
1504 + MemoryArea->SectionData.ViewOffset;
1505 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
1506 /*
1507 * An MM_WAIT_ENTRY is ok in this case... It'll just count as
1508 * IS_SWAP_FROM_SSE and we'll do the right thing.
1509 */
1510 Page = MmGetPfnForProcess(Process, Address);
1511
1512 /* Choose protection based on what was requested */
1513 if (NewProtect == PAGE_EXECUTE_READWRITE)
1514 Protect = PAGE_EXECUTE_READ;
1515 else
1516 Protect = PAGE_READONLY;
1517
1518 if (IS_SWAP_FROM_SSE(Entry) || PFN_FROM_SSE(Entry) != Page)
1519 {
1520 Protect = NewProtect;
1521 }
1522 }
1523
1524 if (MmIsPagePresent(Process, Address) || MmIsDisabledPage(Process, Address))
1525 {
1526 MmSetPageProtect(Process, Address,
1527 Protect);
1528 }
1529 }
1530 }
1531
1532 MmUnlockSectionSegment(Segment);
1533 }
1534
1535 NTSTATUS
1536 NTAPI
MmNotPresentFaultSectionView(PMMSUPPORT AddressSpace,MEMORY_AREA * MemoryArea,PVOID Address,BOOLEAN Locked)1537 MmNotPresentFaultSectionView(PMMSUPPORT AddressSpace,
1538 MEMORY_AREA* MemoryArea,
1539 PVOID Address,
1540 BOOLEAN Locked)
1541 {
1542 LARGE_INTEGER Offset;
1543 PFN_NUMBER Page;
1544 NTSTATUS Status;
1545 PMM_SECTION_SEGMENT Segment;
1546 ULONG_PTR Entry;
1547 ULONG_PTR Entry1;
1548 ULONG Attributes;
1549 PMM_REGION Region;
1550 BOOLEAN HasSwapEntry;
1551 PVOID PAddress;
1552 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
1553 SWAPENTRY SwapEntry;
1554
1555 ASSERT(Locked);
1556
1557 /*
1558 * There is a window between taking the page fault and locking the
1559 * address space when another thread could load the page so we check
1560 * that.
1561 */
1562 if (MmIsPagePresent(Process, Address))
1563 {
1564 return STATUS_SUCCESS;
1565 }
1566
1567 if (MmIsDisabledPage(Process, Address))
1568 {
1569 return STATUS_ACCESS_VIOLATION;
1570 }
1571
1572 /*
1573 * Check for the virtual memory area being deleted.
1574 */
1575 if (MemoryArea->DeleteInProgress)
1576 {
1577 return STATUS_UNSUCCESSFUL;
1578 }
1579
1580 PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1581 Offset.QuadPart = (ULONG_PTR)PAddress - MA_GetStartingAddress(MemoryArea)
1582 + MemoryArea->SectionData.ViewOffset;
1583
1584 Segment = MemoryArea->SectionData.Segment;
1585 Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
1586 &MemoryArea->SectionData.RegionListHead,
1587 Address, NULL);
1588 ASSERT(Region != NULL);
1589
1590 /* Check for a NOACCESS mapping */
1591 if (Region->Protect & PAGE_NOACCESS)
1592 {
1593 return STATUS_ACCESS_VIOLATION;
1594 }
1595
1596 if (Region->Protect & PAGE_GUARD)
1597 {
1598 /* Remove it */
1599 Status = MmAlterRegion(AddressSpace, (PVOID)MA_GetStartingAddress(MemoryArea),
1600 &MemoryArea->SectionData.RegionListHead,
1601 Address, PAGE_SIZE, Region->Type, Region->Protect & ~PAGE_GUARD,
1602 MmAlterViewAttributes);
1603
1604 if (!NT_SUCCESS(Status))
1605 {
1606 DPRINT1("Removing PAGE_GUARD protection failed : 0x%08x.\n", Status);
1607 }
1608
1609 return STATUS_GUARD_PAGE_VIOLATION;
1610 }
1611
1612 HasSwapEntry = MmIsPageSwapEntry(Process, Address);
1613
1614 /* See if we should use a private page */
1615 if (HasSwapEntry)
1616 {
1617 SWAPENTRY DummyEntry;
1618
1619 MmGetPageFileMapping(Process, Address, &SwapEntry);
1620 if (SwapEntry == MM_WAIT_ENTRY)
1621 {
1622 MmUnlockAddressSpace(AddressSpace);
1623 KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
1624 MmLockAddressSpace(AddressSpace);
1625 return STATUS_MM_RESTART_OPERATION;
1626 }
1627
1628 MI_SET_USAGE(MI_USAGE_SECTION);
1629 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1630 if (!Process) MI_SET_PROCESS2("Kernel Section");
1631 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
1632 if (!NT_SUCCESS(Status))
1633 {
1634 return STATUS_NO_MEMORY;
1635 }
1636
1637 /*
1638 * Must be private page we have swapped out.
1639 */
1640
1641 /*
1642 * Sanity check
1643 */
1644 MmDeletePageFileMapping(Process, Address, &DummyEntry);
1645 ASSERT(DummyEntry == SwapEntry);
1646
1647 /* Tell everyone else we are serving the fault. */
1648 MmCreatePageFileMapping(Process, Address, MM_WAIT_ENTRY);
1649
1650 MmUnlockAddressSpace(AddressSpace);
1651
1652 Status = MmReadFromSwapPage(SwapEntry, Page);
1653 if (!NT_SUCCESS(Status))
1654 {
1655 DPRINT1("MmReadFromSwapPage failed, status = %x\n", Status);
1656 KeBugCheck(MEMORY_MANAGEMENT);
1657 }
1658
1659 MmLockAddressSpace(AddressSpace);
1660 MmDeletePageFileMapping(Process, PAddress, &DummyEntry);
1661 ASSERT(DummyEntry == MM_WAIT_ENTRY);
1662
1663 Status = MmCreateVirtualMapping(Process,
1664 PAddress,
1665 Region->Protect,
1666 Page);
1667 if (!NT_SUCCESS(Status))
1668 {
1669 DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
1670 KeBugCheck(MEMORY_MANAGEMENT);
1671 return Status;
1672 }
1673
1674 /*
1675 * Store the swap entry for later use.
1676 */
1677 MmSetSavedSwapEntryPage(Page, SwapEntry);
1678
1679 /*
1680 * Add the page to the process's working set
1681 */
1682 if (Process) MmInsertRmap(Page, Process, Address);
1683 /*
1684 * Finish the operation
1685 */
1686 DPRINT("Address 0x%p\n", Address);
1687 return STATUS_SUCCESS;
1688 }
1689
1690 /*
1691 * Lock the segment
1692 */
1693 MmLockSectionSegment(Segment);
1694
1695 /*
1696 * Satisfying a page fault on a map of /Device/PhysicalMemory is easy
1697 */
1698 if ((*Segment->Flags) & MM_PHYSICALMEMORY_SEGMENT)
1699 {
1700 MmUnlockSectionSegment(Segment);
1701 /*
1702 * Just map the desired physical page
1703 */
1704 Page = (PFN_NUMBER)(Offset.QuadPart >> PAGE_SHIFT);
1705 Status = MmCreatePhysicalMapping(Process,
1706 PAddress,
1707 Region->Protect,
1708 Page);
1709 if (!NT_SUCCESS(Status))
1710 {
1711 DPRINT("MmCreateVirtualMappingUnsafe failed, not out of memory\n");
1712 KeBugCheck(MEMORY_MANAGEMENT);
1713 return Status;
1714 }
1715
1716 /*
1717 * Cleanup and release locks
1718 */
1719 DPRINT("Address 0x%p\n", Address);
1720 return STATUS_SUCCESS;
1721 }
1722
1723 /*
1724 * Check if this page needs to be mapped COW
1725 */
1726 if ((Segment->WriteCopy) &&
1727 (Region->Protect == PAGE_READWRITE || Region->Protect == PAGE_EXECUTE_READWRITE))
1728 {
1729 Attributes = Region->Protect == PAGE_READWRITE ? PAGE_READONLY : PAGE_EXECUTE_READ;
1730 }
1731 else
1732 {
1733 Attributes = Region->Protect;
1734 }
1735
1736
1737 /*
1738 * Get the entry corresponding to the offset within the section
1739 */
1740 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
1741 if (Entry == 0)
1742 {
1743 /*
1744 * If the entry is zero, then we need to load the page.
1745 */
1746 if ((Offset.QuadPart >= (LONGLONG)PAGE_ROUND_UP(Segment->RawLength.QuadPart)) && (MemoryArea->VadNode.u.VadFlags.VadType == VadImageMap))
1747 {
1748 /* We are beyond the data which is on file. Just get a new page. */
1749 MI_SET_USAGE(MI_USAGE_SECTION);
1750 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1751 if (!Process) MI_SET_PROCESS2("Kernel Section");
1752 Status = MmRequestPageMemoryConsumer(MC_USER, FALSE, &Page);
1753 if (!NT_SUCCESS(Status))
1754 {
1755 MmUnlockSectionSegment(Segment);
1756 return STATUS_NO_MEMORY;
1757 }
1758 MmSetPageEntrySectionSegment(Segment, &Offset, MAKE_SSE(Page << PAGE_SHIFT, 1));
1759 MmUnlockSectionSegment(Segment);
1760
1761 Status = MmCreateVirtualMapping(Process, PAddress, Attributes, Page);
1762 if (!NT_SUCCESS(Status))
1763 {
1764 DPRINT1("Unable to create virtual mapping\n");
1765 KeBugCheck(MEMORY_MANAGEMENT);
1766 }
1767 ASSERT(MmIsPagePresent(Process, PAddress));
1768 if (Process)
1769 MmInsertRmap(Page, Process, Address);
1770
1771 DPRINT("Address 0x%p\n", Address);
1772 return STATUS_SUCCESS;
1773 }
1774
1775 MmUnlockSectionSegment(Segment);
1776 MmUnlockAddressSpace(AddressSpace);
1777
1778 /* The data must be paged in. Lock the file, so that the VDL doesn't get updated behind us. */
1779 FsRtlAcquireFileExclusive(Segment->FileObject);
1780
1781 PFSRTL_COMMON_FCB_HEADER FcbHeader = Segment->FileObject->FsContext;
1782
1783 Status = MmMakeSegmentResident(Segment, Offset.QuadPart, PAGE_SIZE, &FcbHeader->ValidDataLength, FALSE);
1784
1785 FsRtlReleaseFile(Segment->FileObject);
1786
1787 /* Lock address space again */
1788 MmLockAddressSpace(AddressSpace);
1789 if (!NT_SUCCESS(Status))
1790 {
1791 if (Status == STATUS_NO_MEMORY)
1792 {
1793 return Status;
1794 }
1795 /* Damn */
1796 DPRINT1("Failed to page data in!\n");
1797 return STATUS_IN_PAGE_ERROR;
1798 }
1799
1800 /* Everything went fine. Restart the operation */
1801 return STATUS_MM_RESTART_OPERATION;
1802 }
1803 else if (IS_SWAP_FROM_SSE(Entry))
1804 {
1805 SWAPENTRY SwapEntry;
1806
1807 SwapEntry = SWAPENTRY_FROM_SSE(Entry);
1808
1809 /* See if a page op is running on this segment. */
1810 if (SwapEntry == MM_WAIT_ENTRY)
1811 {
1812 MmUnlockSectionSegment(Segment);
1813 MmUnlockAddressSpace(AddressSpace);
1814 KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
1815 MmLockAddressSpace(AddressSpace);
1816 return STATUS_MM_RESTART_OPERATION;
1817 }
1818
1819 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
1820 if (!NT_SUCCESS(Status))
1821 {
1822 MmUnlockSectionSegment(Segment);
1823 return STATUS_NO_MEMORY;
1824 }
1825
1826 /*
1827 * Release all our locks and read in the page from disk
1828 */
1829 MmSetPageEntrySectionSegment(Segment, &Offset, MAKE_SWAP_SSE(MM_WAIT_ENTRY));
1830 MmUnlockSectionSegment(Segment);
1831
1832 MmUnlockAddressSpace(AddressSpace);
1833
1834 Status = MmReadFromSwapPage(SwapEntry, Page);
1835 if (!NT_SUCCESS(Status))
1836 {
1837 KeBugCheck(MEMORY_MANAGEMENT);
1838 }
1839
1840 /*
1841 * Relock the address space and segment
1842 */
1843 MmLockAddressSpace(AddressSpace);
1844 MmLockSectionSegment(Segment);
1845
1846 /*
1847 * Check the entry. No one should change the status of a page
1848 * that has a pending page-in.
1849 */
1850 Entry1 = MmGetPageEntrySectionSegment(Segment, &Offset);
1851 if (Entry1 != MAKE_SWAP_SSE(MM_WAIT_ENTRY))
1852 {
1853 DPRINT1("Someone changed ppte entry while we slept (%x vs %x)\n", Entry, Entry1);
1854 KeBugCheck(MEMORY_MANAGEMENT);
1855 }
1856
1857 /*
1858 * Save the swap entry.
1859 */
1860 MmSetSavedSwapEntryPage(Page, SwapEntry);
1861
1862 /* Map the page into the process address space */
1863 Status = MmCreateVirtualMapping(Process,
1864 PAddress,
1865 Attributes,
1866 Page);
1867 if (!NT_SUCCESS(Status))
1868 {
1869 DPRINT1("Unable to create virtual mapping\n");
1870 KeBugCheck(MEMORY_MANAGEMENT);
1871 }
1872 if (Process)
1873 MmInsertRmap(Page, Process, Address);
1874
1875 /*
1876 * Mark the offset within the section as having valid, in-memory
1877 * data
1878 */
1879 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
1880 MmSetPageEntrySectionSegment(Segment, &Offset, Entry);
1881 MmUnlockSectionSegment(Segment);
1882
1883 DPRINT("Address 0x%p\n", Address);
1884 return STATUS_SUCCESS;
1885 }
1886 else
1887 {
1888 /* We already have a page on this section offset. Map it into the process address space. */
1889 Page = PFN_FROM_SSE(Entry);
1890
1891 Status = MmCreateVirtualMapping(Process,
1892 PAddress,
1893 Attributes,
1894 Page);
1895 if (!NT_SUCCESS(Status))
1896 {
1897 DPRINT1("Unable to create virtual mapping\n");
1898 KeBugCheck(MEMORY_MANAGEMENT);
1899 }
1900
1901 if (Process)
1902 MmInsertRmap(Page, Process, Address);
1903
1904 /* Take a reference on it */
1905 MmSharePageEntrySectionSegment(Segment, &Offset);
1906 MmUnlockSectionSegment(Segment);
1907
1908 DPRINT("Address 0x%p\n", Address);
1909 return STATUS_SUCCESS;
1910 }
1911 }
1912
1913 NTSTATUS
1914 NTAPI
MmAccessFaultSectionView(PMMSUPPORT AddressSpace,MEMORY_AREA * MemoryArea,PVOID Address,BOOLEAN Locked)1915 MmAccessFaultSectionView(PMMSUPPORT AddressSpace,
1916 MEMORY_AREA* MemoryArea,
1917 PVOID Address,
1918 BOOLEAN Locked)
1919 {
1920 PMM_SECTION_SEGMENT Segment;
1921 PFN_NUMBER OldPage;
1922 PFN_NUMBER NewPage;
1923 PFN_NUMBER UnmappedPage;
1924 PVOID PAddress;
1925 LARGE_INTEGER Offset;
1926 PMM_REGION Region;
1927 ULONG_PTR Entry;
1928 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
1929 BOOLEAN Cow = FALSE;
1930 ULONG NewProtect;
1931 BOOLEAN Unmapped;
1932 NTSTATUS Status;
1933
1934 DPRINT("MmAccessFaultSectionView(%p, %p, %p)\n", AddressSpace, MemoryArea, Address);
1935
1936 /* Get the region for this address */
1937 Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
1938 &MemoryArea->SectionData.RegionListHead,
1939 Address, NULL);
1940 ASSERT(Region != NULL);
1941 if (!(Region->Protect & PAGE_IS_WRITABLE))
1942 return STATUS_ACCESS_VIOLATION;
1943
1944 /* Make sure we have a page mapping for this address. */
1945 if (!MmIsPagePresent(Process, Address))
1946 {
1947 NTSTATUS Status = MmNotPresentFaultSectionView(AddressSpace, MemoryArea, Address, Locked);
1948 if (!NT_SUCCESS(Status))
1949 {
1950 /* This is invalid access ! */
1951 return Status;
1952 }
1953 }
1954
1955 /*
1956 * Check if the page has already been set readwrite
1957 */
1958 if (MmGetPageProtect(Process, Address) & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE))
1959 {
1960 DPRINT("Address 0x%p\n", Address);
1961 return STATUS_SUCCESS;
1962 }
1963
1964 /* Check if we are doing Copy-On-Write */
1965 Segment = MemoryArea->SectionData.Segment;
1966 Cow = Segment->WriteCopy || (Region->Protect & PAGE_IS_WRITECOPY);
1967
1968 if (!Cow)
1969 {
1970 /* Simply update page protection and we're done */
1971 MmSetPageProtect(Process, Address, Region->Protect);
1972 return STATUS_SUCCESS;
1973 }
1974
1975 /* Calculate the new protection & check if we should update the region */
1976 NewProtect = Region->Protect;
1977 if (NewProtect & PAGE_IS_WRITECOPY)
1978 {
1979 NewProtect &= ~PAGE_IS_WRITECOPY;
1980 if (Region->Protect & PAGE_IS_EXECUTABLE)
1981 NewProtect |= PAGE_EXECUTE_READWRITE;
1982 else
1983 NewProtect |= PAGE_READWRITE;
1984 MmAlterRegion(AddressSpace, (PVOID)MA_GetStartingAddress(MemoryArea),
1985 &MemoryArea->SectionData.RegionListHead,
1986 Address, PAGE_SIZE, Region->Type, NewProtect,
1987 MmAlterViewAttributes);
1988 }
1989
1990 /*
1991 * Find the offset of the page
1992 */
1993 PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1994 Offset.QuadPart = (ULONG_PTR)PAddress - MA_GetStartingAddress(MemoryArea)
1995 + MemoryArea->SectionData.ViewOffset;
1996
1997 /* Get the page mapping this section offset. */
1998 MmLockSectionSegment(Segment);
1999 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
2000
2001 /* Get the current page mapping for the process */
2002 ASSERT(MmIsPagePresent(Process, PAddress));
2003 OldPage = MmGetPfnForProcess(Process, PAddress);
2004 ASSERT(OldPage != 0);
2005
2006 if (IS_SWAP_FROM_SSE(Entry) ||
2007 PFN_FROM_SSE(Entry) != OldPage)
2008 {
2009 MmUnlockSectionSegment(Segment);
2010 /* This is a private page. We must only change the page protection. */
2011 MmSetPageProtect(Process, PAddress, NewProtect);
2012 return STATUS_SUCCESS;
2013 }
2014
2015 /*
2016 * Allocate a page
2017 */
2018 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &NewPage);
2019 if (!NT_SUCCESS(Status))
2020 {
2021 MmUnlockSectionSegment(Segment);
2022 return STATUS_NO_MEMORY;
2023 }
2024
2025 /*
2026 * Copy the old page
2027 */
2028 NT_VERIFY(NT_SUCCESS(MiCopyFromUserPage(NewPage, PAddress)));
2029
2030 /*
2031 * Unshare the old page.
2032 */
2033 DPRINT("Swapping page (Old %x New %x)\n", OldPage, NewPage);
2034 Unmapped = MmDeleteVirtualMapping(Process, PAddress, NULL, &UnmappedPage);
2035 if (!Unmapped || (UnmappedPage != OldPage))
2036 {
2037 /* Uh , we had a page just before, but suddenly it changes. Someone corrupted us. */
2038 KeBugCheckEx(MEMORY_MANAGEMENT,
2039 (ULONG_PTR)Process,
2040 (ULONG_PTR)PAddress,
2041 (ULONG_PTR)__FILE__,
2042 __LINE__);
2043 }
2044
2045 if (Process)
2046 MmDeleteRmap(OldPage, Process, PAddress);
2047 MmUnsharePageEntrySectionSegment(MemoryArea, Segment, &Offset, FALSE, FALSE, NULL);
2048 MmUnlockSectionSegment(Segment);
2049
2050 /*
2051 * Set the PTE to point to the new page
2052 */
2053 if (!NT_SUCCESS(MmCreateVirtualMapping(Process, PAddress, NewProtect, NewPage)))
2054 {
2055 DPRINT1("MmCreateVirtualMapping failed, unable to create virtual mapping, not out of memory\n");
2056 KeBugCheck(MEMORY_MANAGEMENT);
2057 }
2058
2059 if (Process)
2060 MmInsertRmap(NewPage, Process, PAddress);
2061
2062 DPRINT("Address 0x%p\n", Address);
2063 return STATUS_SUCCESS;
2064 }
2065
2066 NTSTATUS
2067 NTAPI
MmProtectSectionView(PMMSUPPORT AddressSpace,PMEMORY_AREA MemoryArea,PVOID BaseAddress,SIZE_T Length,ULONG Protect,PULONG OldProtect)2068 MmProtectSectionView(PMMSUPPORT AddressSpace,
2069 PMEMORY_AREA MemoryArea,
2070 PVOID BaseAddress,
2071 SIZE_T Length,
2072 ULONG Protect,
2073 PULONG OldProtect)
2074 {
2075 PMM_REGION Region;
2076 NTSTATUS Status;
2077 ULONG_PTR MaxLength;
2078
2079 MaxLength = MA_GetEndingAddress(MemoryArea) - (ULONG_PTR)BaseAddress;
2080 if (Length > MaxLength)
2081 Length = (ULONG)MaxLength;
2082
2083 Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
2084 &MemoryArea->SectionData.RegionListHead,
2085 BaseAddress, NULL);
2086 ASSERT(Region != NULL);
2087
2088 if ((MemoryArea->Flags & SEC_NO_CHANGE) &&
2089 Region->Protect != Protect)
2090 {
2091 return STATUS_INVALID_PAGE_PROTECTION;
2092 }
2093
2094 *OldProtect = Region->Protect;
2095 Status = MmAlterRegion(AddressSpace, (PVOID)MA_GetStartingAddress(MemoryArea),
2096 &MemoryArea->SectionData.RegionListHead,
2097 BaseAddress, Length, Region->Type, Protect,
2098 MmAlterViewAttributes);
2099
2100 return Status;
2101 }
2102
2103 NTSTATUS NTAPI
MmQuerySectionView(PMEMORY_AREA MemoryArea,PVOID Address,PMEMORY_BASIC_INFORMATION Info,PSIZE_T ResultLength)2104 MmQuerySectionView(PMEMORY_AREA MemoryArea,
2105 PVOID Address,
2106 PMEMORY_BASIC_INFORMATION Info,
2107 PSIZE_T ResultLength)
2108 {
2109 PMM_REGION Region;
2110 PVOID RegionBaseAddress;
2111 PMM_SECTION_SEGMENT Segment;
2112
2113 Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
2114 &MemoryArea->SectionData.RegionListHead,
2115 Address, &RegionBaseAddress);
2116 if (Region == NULL)
2117 {
2118 return STATUS_UNSUCCESSFUL;
2119 }
2120
2121 if (MemoryArea->VadNode.u.VadFlags.VadType == VadImageMap)
2122 {
2123 Segment = MemoryArea->SectionData.Segment;
2124 Info->AllocationBase = (PUCHAR)MA_GetStartingAddress(MemoryArea) - Segment->Image.VirtualAddress;
2125 Info->Type = MEM_IMAGE;
2126 }
2127 else
2128 {
2129 Info->AllocationBase = (PVOID)MA_GetStartingAddress(MemoryArea);
2130 Info->Type = MEM_MAPPED;
2131 }
2132 Info->BaseAddress = RegionBaseAddress;
2133 Info->AllocationProtect = MmProtectToValue[MemoryArea->VadNode.u.VadFlags.Protection];
2134 Info->RegionSize = Region->Length;
2135 Info->State = MEM_COMMIT;
2136 Info->Protect = Region->Protect;
2137
2138 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
2139 return STATUS_SUCCESS;
2140 }
2141
2142 VOID NTAPI
MmpDeleteSection(PVOID ObjectBody)2143 MmpDeleteSection(PVOID ObjectBody)
2144 {
2145 PSECTION Section = ObjectBody;
2146
2147 /* Check if it's an ARM3, or ReactOS section */
2148 if (!MiIsRosSectionObject(Section))
2149 {
2150 MiDeleteARM3Section(ObjectBody);
2151 return;
2152 }
2153
2154 DPRINT("MmpDeleteSection(ObjectBody %p)\n", ObjectBody);
2155 if (Section->u.Flags.Image)
2156 {
2157 PMM_IMAGE_SECTION_OBJECT ImageSectionObject = (PMM_IMAGE_SECTION_OBJECT)Section->Segment;
2158
2159 /*
2160 * NOTE: Section->ImageSection can be NULL for short time
2161 * during the section creating. If we fail for some reason
2162 * until the image section is properly initialized we shouldn't
2163 * process further here.
2164 */
2165 if (Section->Segment == NULL)
2166 return;
2167
2168 KIRQL OldIrql = MiAcquirePfnLock();
2169 ImageSectionObject->SectionCount--;
2170
2171 /* We just dereference the first segment */
2172 ASSERT(ImageSectionObject->RefCount > 0);
2173 /* MmDereferenceSegmentWithLock releases PFN lock */
2174 MmDereferenceSegmentWithLock(ImageSectionObject->Segments, OldIrql);
2175 }
2176 else
2177 {
2178 PMM_SECTION_SEGMENT Segment = (PMM_SECTION_SEGMENT)Section->Segment;
2179
2180 /*
2181 * NOTE: Section->Segment can be NULL for short time
2182 * during the section creating.
2183 */
2184 if (Segment == NULL)
2185 return;
2186
2187 KIRQL OldIrql = MiAcquirePfnLock();
2188 Segment->SectionCount--;
2189
2190 /* MmDereferenceSegmentWithLock releases PFN lock */
2191 MmDereferenceSegmentWithLock(Segment, OldIrql);
2192 }
2193 }
2194
2195 VOID NTAPI
MmpCloseSection(IN PEPROCESS Process OPTIONAL,IN PVOID Object,IN ACCESS_MASK GrantedAccess,IN ULONG ProcessHandleCount,IN ULONG SystemHandleCount)2196 MmpCloseSection(IN PEPROCESS Process OPTIONAL,
2197 IN PVOID Object,
2198 IN ACCESS_MASK GrantedAccess,
2199 IN ULONG ProcessHandleCount,
2200 IN ULONG SystemHandleCount)
2201 {
2202 DPRINT("MmpCloseSection(OB %p, HC %lu)\n", Object, ProcessHandleCount);
2203 }
2204
2205 CODE_SEG("INIT")
2206 NTSTATUS
2207 NTAPI
MmCreatePhysicalMemorySection(VOID)2208 MmCreatePhysicalMemorySection(VOID)
2209 {
2210 PSECTION PhysSection;
2211 NTSTATUS Status;
2212 OBJECT_ATTRIBUTES Obj;
2213 UNICODE_STRING Name = RTL_CONSTANT_STRING(L"\\Device\\PhysicalMemory");
2214 LARGE_INTEGER SectionSize;
2215 HANDLE Handle;
2216 PMM_SECTION_SEGMENT Segment;
2217
2218 /*
2219 * Create the section mapping physical memory
2220 */
2221 SectionSize.QuadPart = MmHighestPhysicalPage * PAGE_SIZE;
2222 InitializeObjectAttributes(&Obj,
2223 &Name,
2224 OBJ_PERMANENT | OBJ_KERNEL_EXCLUSIVE,
2225 NULL,
2226 NULL);
2227 /*
2228 * Create the Object
2229 */
2230 Status = ObCreateObject(KernelMode,
2231 MmSectionObjectType,
2232 &Obj,
2233 ExGetPreviousMode(),
2234 NULL,
2235 sizeof(*PhysSection),
2236 0,
2237 0,
2238 (PVOID*)&PhysSection);
2239 if (!NT_SUCCESS(Status))
2240 {
2241 DPRINT1("MmCreatePhysicalMemorySection: failed to create object (0x%lx)\n", Status);
2242 return Status;
2243 }
2244
2245 /*
2246 * Initialize it
2247 */
2248 RtlZeroMemory(PhysSection, sizeof(*PhysSection));
2249
2250 /* Mark this as a "ROS Section" */
2251 PhysSection->u.Flags.filler = 1;
2252 PhysSection->InitialPageProtection = PAGE_EXECUTE_READWRITE;
2253 PhysSection->u.Flags.PhysicalMemory = 1;
2254 PhysSection->SizeOfSection = SectionSize;
2255 Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
2256 TAG_MM_SECTION_SEGMENT);
2257 if (Segment == NULL)
2258 {
2259 ObDereferenceObject(PhysSection);
2260 return STATUS_NO_MEMORY;
2261 }
2262 RtlZeroMemory(Segment, sizeof(MM_SECTION_SEGMENT));
2263 PhysSection->Segment = (PSEGMENT)Segment;
2264 Segment->RefCount = 1;
2265
2266 Segment->ReferenceCount = &Segment->RefCount;
2267 Segment->Flags = &Segment->SegFlags;
2268
2269 ExInitializeFastMutex(&Segment->Lock);
2270 Segment->Image.FileOffset = 0;
2271 Segment->Protection = PAGE_EXECUTE_READWRITE;
2272 Segment->RawLength = SectionSize;
2273 Segment->Length = SectionSize;
2274 Segment->SegFlags = MM_PHYSICALMEMORY_SEGMENT;
2275 Segment->WriteCopy = FALSE;
2276 Segment->Image.VirtualAddress = 0;
2277 Segment->Image.Characteristics = 0;
2278 MiInitializeSectionPageTable(Segment);
2279
2280 Status = ObInsertObject(PhysSection,
2281 NULL,
2282 SECTION_ALL_ACCESS,
2283 0,
2284 NULL,
2285 &Handle);
2286 if (!NT_SUCCESS(Status))
2287 {
2288 ObDereferenceObject(PhysSection);
2289 return Status;
2290 }
2291 ObCloseHandle(Handle, KernelMode);
2292
2293 return STATUS_SUCCESS;
2294 }
2295
2296 CODE_SEG("INIT")
2297 NTSTATUS
2298 NTAPI
MmInitSectionImplementation(VOID)2299 MmInitSectionImplementation(VOID)
2300 {
2301 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
2302 UNICODE_STRING Name;
2303
2304 DPRINT("Creating Section Object Type\n");
2305
2306 /* Initialize the section based root */
2307 ASSERT(MmSectionBasedRoot.NumberGenericTableElements == 0);
2308 MmSectionBasedRoot.BalancedRoot.u1.Parent = &MmSectionBasedRoot.BalancedRoot;
2309
2310 /* Initialize the Section object type */
2311 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
2312 RtlInitUnicodeString(&Name, L"Section");
2313 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
2314 ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(SECTION);
2315 ObjectTypeInitializer.PoolType = PagedPool;
2316 ObjectTypeInitializer.UseDefaultObject = TRUE;
2317 ObjectTypeInitializer.GenericMapping = MmpSectionMapping;
2318 ObjectTypeInitializer.DeleteProcedure = MmpDeleteSection;
2319 ObjectTypeInitializer.CloseProcedure = MmpCloseSection;
2320 ObjectTypeInitializer.ValidAccessMask = SECTION_ALL_ACCESS;
2321 ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
2322 ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &MmSectionObjectType);
2323
2324 MmCreatePhysicalMemorySection();
2325
2326 return STATUS_SUCCESS;
2327 }
2328
2329 static
2330 NTSTATUS
2331 NTAPI
MmCreateDataFileSection(PSECTION * SectionObject,ACCESS_MASK DesiredAccess,POBJECT_ATTRIBUTES ObjectAttributes,PLARGE_INTEGER UMaximumSize,ULONG SectionPageProtection,ULONG AllocationAttributes,PFILE_OBJECT FileObject,BOOLEAN GotFileHandle)2332 MmCreateDataFileSection(PSECTION *SectionObject,
2333 ACCESS_MASK DesiredAccess,
2334 POBJECT_ATTRIBUTES ObjectAttributes,
2335 PLARGE_INTEGER UMaximumSize,
2336 ULONG SectionPageProtection,
2337 ULONG AllocationAttributes,
2338 PFILE_OBJECT FileObject,
2339 BOOLEAN GotFileHandle)
2340 /*
2341 * Create a section backed by a data file
2342 */
2343 {
2344 PSECTION Section;
2345 NTSTATUS Status;
2346 LARGE_INTEGER MaximumSize;
2347 PMM_SECTION_SEGMENT Segment;
2348 KIRQL OldIrql;
2349
2350 /*
2351 * Create the section
2352 */
2353 Status = ObCreateObject(ExGetPreviousMode(),
2354 MmSectionObjectType,
2355 ObjectAttributes,
2356 ExGetPreviousMode(),
2357 NULL,
2358 sizeof(*Section),
2359 0,
2360 0,
2361 (PVOID*)&Section);
2362 if (!NT_SUCCESS(Status))
2363 {
2364 return Status;
2365 }
2366 /*
2367 * Initialize it
2368 */
2369 RtlZeroMemory(Section, sizeof(*Section));
2370
2371 /* Mark this as a "ROS" section */
2372 Section->u.Flags.filler = 1;
2373 Section->InitialPageProtection = SectionPageProtection;
2374 Section->u.Flags.File = 1;
2375
2376 if (AllocationAttributes & SEC_NO_CHANGE)
2377 Section->u.Flags.NoChange = 1;
2378 if (AllocationAttributes & SEC_RESERVE)
2379 Section->u.Flags.Reserve = 1;
2380
2381 if (!GotFileHandle)
2382 {
2383 ASSERT(UMaximumSize != NULL);
2384 // ASSERT(UMaximumSize->QuadPart != 0);
2385 MaximumSize = *UMaximumSize;
2386 }
2387 else
2388 {
2389 LARGE_INTEGER FileSize;
2390 Status = FsRtlGetFileSize(FileObject, &FileSize);
2391 if (!NT_SUCCESS(Status))
2392 {
2393 ObDereferenceObject(Section);
2394 return Status;
2395 }
2396
2397 /*
2398 * FIXME: Revise this once a locking order for file size changes is
2399 * decided
2400 */
2401 if ((UMaximumSize != NULL) && (UMaximumSize->QuadPart != 0))
2402 {
2403 MaximumSize = *UMaximumSize;
2404 }
2405 else
2406 {
2407 MaximumSize = FileSize;
2408 /* Mapping zero-sized files isn't allowed. */
2409 if (MaximumSize.QuadPart == 0)
2410 {
2411 ObDereferenceObject(Section);
2412 return STATUS_MAPPED_FILE_SIZE_ZERO;
2413 }
2414 }
2415
2416 if (MaximumSize.QuadPart > FileSize.QuadPart)
2417 {
2418 Status = IoSetInformation(FileObject,
2419 FileEndOfFileInformation,
2420 sizeof(LARGE_INTEGER),
2421 &MaximumSize);
2422 if (!NT_SUCCESS(Status))
2423 {
2424 ObDereferenceObject(Section);
2425 return STATUS_SECTION_NOT_EXTENDED;
2426 }
2427 }
2428 }
2429
2430 if (FileObject->SectionObjectPointer == NULL)
2431 {
2432 ObDereferenceObject(Section);
2433 return STATUS_INVALID_FILE_FOR_SECTION;
2434 }
2435
2436 /*
2437 * Lock the file
2438 */
2439 Status = MmspWaitForFileLock(FileObject);
2440 if (Status != STATUS_SUCCESS)
2441 {
2442 ObDereferenceObject(Section);
2443 return Status;
2444 }
2445
2446 /* Lock the PFN lock while messing with Section Object pointers */
2447 grab_segment:
2448 OldIrql = MiAcquirePfnLock();
2449 Segment = FileObject->SectionObjectPointer->DataSectionObject;
2450
2451 while (Segment && (Segment->SegFlags & (MM_SEGMENT_INDELETE | MM_SEGMENT_INCREATE)))
2452 {
2453 MiReleasePfnLock(OldIrql);
2454 KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
2455 OldIrql = MiAcquirePfnLock();
2456 Segment = FileObject->SectionObjectPointer->DataSectionObject;
2457 }
2458
2459 /*
2460 * If this file hasn't been mapped as a data file before then allocate a
2461 * section segment to describe the data file mapping
2462 */
2463 if (Segment == NULL)
2464 {
2465 /* Release the lock. ExAllocatePoolWithTag might acquire it */
2466 MiReleasePfnLock(OldIrql);
2467
2468 Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
2469 TAG_MM_SECTION_SEGMENT);
2470 if (Segment == NULL)
2471 {
2472 //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
2473 ObDereferenceObject(Section);
2474 return STATUS_NO_MEMORY;
2475 }
2476
2477 /* We are creating it */
2478 RtlZeroMemory(Segment, sizeof(*Segment));
2479 Segment->SegFlags = MM_DATAFILE_SEGMENT | MM_SEGMENT_INCREATE;
2480 Segment->RefCount = 1;
2481
2482 /* Acquire lock again */
2483 OldIrql = MiAcquirePfnLock();
2484
2485 if (FileObject->SectionObjectPointer->DataSectionObject != NULL)
2486 {
2487 /* Well that's bad luck. Restart it all over */
2488 MiReleasePfnLock(OldIrql);
2489 ExFreePoolWithTag(Segment, TAG_MM_SECTION_SEGMENT);
2490 goto grab_segment;
2491 }
2492
2493 FileObject->SectionObjectPointer->DataSectionObject = Segment;
2494
2495 /* We're safe to release the lock now */
2496 MiReleasePfnLock(OldIrql);
2497
2498 Section->Segment = (PSEGMENT)Segment;
2499
2500 /* Self-referencing segment */
2501 Segment->Flags = &Segment->SegFlags;
2502 Segment->ReferenceCount = &Segment->RefCount;
2503
2504 Segment->SectionCount = 1;
2505
2506 ExInitializeFastMutex(&Segment->Lock);
2507 Segment->FileObject = FileObject;
2508 ObReferenceObject(FileObject);
2509
2510 Segment->Image.FileOffset = 0;
2511 Segment->Protection = SectionPageProtection;
2512
2513 Segment->Image.Characteristics = 0;
2514 Segment->WriteCopy = (SectionPageProtection & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY));
2515 if (AllocationAttributes & SEC_RESERVE)
2516 {
2517 Segment->Length.QuadPart = Segment->RawLength.QuadPart = 0;
2518 }
2519 else
2520 {
2521 Segment->RawLength.QuadPart = MaximumSize.QuadPart;
2522 Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
2523 }
2524 Segment->Image.VirtualAddress = 0;
2525 MiInitializeSectionPageTable(Segment);
2526
2527 /* We're good to use it now */
2528 OldIrql = MiAcquirePfnLock();
2529 Segment->SegFlags &= ~MM_SEGMENT_INCREATE;
2530 MiReleasePfnLock(OldIrql);
2531 }
2532 else
2533 {
2534 Section->Segment = (PSEGMENT)Segment;
2535 InterlockedIncrement64(&Segment->RefCount);
2536 InterlockedIncrementUL(&Segment->SectionCount);
2537
2538 MiReleasePfnLock(OldIrql);
2539
2540 MmLockSectionSegment(Segment);
2541
2542 if (MaximumSize.QuadPart > Segment->RawLength.QuadPart &&
2543 !(AllocationAttributes & SEC_RESERVE))
2544 {
2545 Segment->RawLength.QuadPart = MaximumSize.QuadPart;
2546 Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
2547 }
2548
2549 MmUnlockSectionSegment(Segment);
2550 }
2551 Section->SizeOfSection = MaximumSize;
2552
2553 //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
2554 *SectionObject = Section;
2555 return STATUS_SUCCESS;
2556 }
2557
2558 /*
2559 TODO: not that great (declaring loaders statically, having to declare all of
2560 them, having to keep them extern, etc.), will fix in the future
2561 */
2562 extern NTSTATUS NTAPI PeFmtCreateSection
2563 (
2564 IN CONST VOID * FileHeader,
2565 IN SIZE_T FileHeaderSize,
2566 IN PVOID File,
2567 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2568 OUT PULONG Flags,
2569 IN PEXEFMT_CB_READ_FILE ReadFileCb,
2570 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
2571 );
2572
2573 extern NTSTATUS NTAPI ElfFmtCreateSection
2574 (
2575 IN CONST VOID * FileHeader,
2576 IN SIZE_T FileHeaderSize,
2577 IN PVOID File,
2578 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2579 OUT PULONG Flags,
2580 IN PEXEFMT_CB_READ_FILE ReadFileCb,
2581 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
2582 );
2583
2584 static PEXEFMT_LOADER ExeFmtpLoaders[] =
2585 {
2586 PeFmtCreateSection,
2587 #ifdef __ELF
2588 ElfFmtCreateSection
2589 #endif
2590 };
2591
2592 static
2593 PMM_SECTION_SEGMENT
2594 NTAPI
ExeFmtpAllocateSegments(IN ULONG NrSegments)2595 ExeFmtpAllocateSegments(IN ULONG NrSegments)
2596 {
2597 SIZE_T SizeOfSegments;
2598 PMM_SECTION_SEGMENT Segments;
2599
2600 /* TODO: check for integer overflow */
2601 SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * NrSegments;
2602
2603 Segments = ExAllocatePoolWithTag(NonPagedPool,
2604 SizeOfSegments,
2605 TAG_MM_SECTION_SEGMENT);
2606
2607 if(Segments)
2608 RtlZeroMemory(Segments, SizeOfSegments);
2609
2610 return Segments;
2611 }
2612 static
2613 NTSTATUS
2614 NTAPI
ExeFmtpReadFile(IN PVOID File,IN PLARGE_INTEGER Offset,IN ULONG Length,OUT PVOID * Data,OUT PVOID * AllocBase,OUT PULONG ReadSize)2615 ExeFmtpReadFile(IN PVOID File,
2616 IN PLARGE_INTEGER Offset,
2617 IN ULONG Length,
2618 OUT PVOID * Data,
2619 OUT PVOID * AllocBase,
2620 OUT PULONG ReadSize)
2621 {
2622 NTSTATUS Status;
2623 LARGE_INTEGER FileOffset;
2624 ULONG AdjustOffset;
2625 ULONG OffsetAdjustment;
2626 ULONG BufferSize;
2627 ULONG UsedSize;
2628 PVOID Buffer;
2629 PFILE_OBJECT FileObject = File;
2630 IO_STATUS_BLOCK Iosb;
2631
2632 ASSERT_IRQL_LESS(DISPATCH_LEVEL);
2633
2634 if(Length == 0)
2635 {
2636 KeBugCheck(MEMORY_MANAGEMENT);
2637 }
2638
2639 FileOffset = *Offset;
2640
2641 /* Negative/special offset: it cannot be used in this context */
2642 if(FileOffset.u.HighPart < 0)
2643 {
2644 KeBugCheck(MEMORY_MANAGEMENT);
2645 }
2646
2647 AdjustOffset = PAGE_ROUND_DOWN(FileOffset.u.LowPart);
2648 OffsetAdjustment = FileOffset.u.LowPart - AdjustOffset;
2649 FileOffset.u.LowPart = AdjustOffset;
2650
2651 BufferSize = Length + OffsetAdjustment;
2652 BufferSize = PAGE_ROUND_UP(BufferSize);
2653
2654 /*
2655 * It's ok to use paged pool, because this is a temporary buffer only used in
2656 * the loading of executables. The assumption is that MmCreateSection is
2657 * always called at low IRQLs and that these buffers don't survive a brief
2658 * initialization phase
2659 */
2660 Buffer = ExAllocatePoolWithTag(PagedPool, BufferSize, 'rXmM');
2661 if (!Buffer)
2662 {
2663 return STATUS_INSUFFICIENT_RESOURCES;
2664 }
2665
2666 Status = MiSimpleRead(FileObject, &FileOffset, Buffer, BufferSize, TRUE, &Iosb);
2667
2668 UsedSize = (ULONG)Iosb.Information;
2669
2670 if(NT_SUCCESS(Status) && UsedSize < OffsetAdjustment)
2671 {
2672 Status = STATUS_IN_PAGE_ERROR;
2673 ASSERT(!NT_SUCCESS(Status));
2674 }
2675
2676 if(NT_SUCCESS(Status))
2677 {
2678 *Data = (PVOID)((ULONG_PTR)Buffer + OffsetAdjustment);
2679 *AllocBase = Buffer;
2680 *ReadSize = UsedSize - OffsetAdjustment;
2681 }
2682 else
2683 {
2684 ExFreePoolWithTag(Buffer, 'rXmM');
2685 }
2686
2687 return Status;
2688 }
2689
2690 #ifdef NASSERT
2691 # define MmspAssertSegmentsSorted(OBJ_) ((void)0)
2692 # define MmspAssertSegmentsNoOverlap(OBJ_) ((void)0)
2693 # define MmspAssertSegmentsPageAligned(OBJ_) ((void)0)
2694 #else
2695 static
2696 VOID
2697 NTAPI
MmspAssertSegmentsSorted(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)2698 MmspAssertSegmentsSorted(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
2699 {
2700 ULONG i;
2701
2702 for( i = 1; i < ImageSectionObject->NrSegments; ++ i )
2703 {
2704 ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >=
2705 ImageSectionObject->Segments[i - 1].Image.VirtualAddress);
2706 }
2707 }
2708
2709 static
2710 VOID
2711 NTAPI
MmspAssertSegmentsNoOverlap(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)2712 MmspAssertSegmentsNoOverlap(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
2713 {
2714 ULONG i;
2715
2716 MmspAssertSegmentsSorted(ImageSectionObject);
2717
2718 for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2719 {
2720 ASSERT(ImageSectionObject->Segments[i].Length.QuadPart > 0);
2721
2722 if(i > 0)
2723 {
2724 ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >=
2725 (ImageSectionObject->Segments[i - 1].Image.VirtualAddress +
2726 ImageSectionObject->Segments[i - 1].Length.QuadPart));
2727 }
2728 }
2729 }
2730
2731 static
2732 VOID
2733 NTAPI
MmspAssertSegmentsPageAligned(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)2734 MmspAssertSegmentsPageAligned(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
2735 {
2736 ULONG i;
2737
2738 for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2739 {
2740 ASSERT((ImageSectionObject->Segments[i].Image.VirtualAddress % PAGE_SIZE) == 0);
2741 ASSERT((ImageSectionObject->Segments[i].Length.QuadPart % PAGE_SIZE) == 0);
2742 }
2743 }
2744 #endif
2745
2746 static
2747 int
2748 __cdecl
MmspCompareSegments(const void * x,const void * y)2749 MmspCompareSegments(const void * x,
2750 const void * y)
2751 {
2752 const MM_SECTION_SEGMENT *Segment1 = (const MM_SECTION_SEGMENT *)x;
2753 const MM_SECTION_SEGMENT *Segment2 = (const MM_SECTION_SEGMENT *)y;
2754
2755 if (Segment1->Image.VirtualAddress > Segment2->Image.VirtualAddress)
2756 return 1;
2757 else if (Segment1->Image.VirtualAddress < Segment2->Image.VirtualAddress)
2758 return -1;
2759 else
2760 return 0;
2761 }
2762
2763 /*
2764 * Ensures an image section's segments are sorted in memory
2765 */
2766 static
2767 VOID
2768 NTAPI
MmspSortSegments(IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,IN ULONG Flags)2769 MmspSortSegments(IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2770 IN ULONG Flags)
2771 {
2772 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_SORTED)
2773 {
2774 MmspAssertSegmentsSorted(ImageSectionObject);
2775 }
2776 else
2777 {
2778 qsort(ImageSectionObject->Segments,
2779 ImageSectionObject->NrSegments,
2780 sizeof(ImageSectionObject->Segments[0]),
2781 MmspCompareSegments);
2782 }
2783 }
2784
2785
2786 /*
2787 * Ensures an image section's segments don't overlap in memory and don't have
2788 * gaps and don't have a null size. We let them map to overlapping file regions,
2789 * though - that's not necessarily an error
2790 */
2791 static
2792 BOOLEAN
2793 NTAPI
MmspCheckSegmentBounds(IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,IN ULONG Flags)2794 MmspCheckSegmentBounds
2795 (
2796 IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2797 IN ULONG Flags
2798 )
2799 {
2800 ULONG i;
2801
2802 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_NO_OVERLAP)
2803 {
2804 MmspAssertSegmentsNoOverlap(ImageSectionObject);
2805 return TRUE;
2806 }
2807
2808 ASSERT(ImageSectionObject->NrSegments >= 1);
2809
2810 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2811 {
2812 if(ImageSectionObject->Segments[i].Length.QuadPart == 0)
2813 {
2814 return FALSE;
2815 }
2816
2817 if(i > 0)
2818 {
2819 /*
2820 * TODO: relax the limitation on gaps. For example, gaps smaller than a
2821 * page could be OK (Windows seems to be OK with them), and larger gaps
2822 * could lead to image sections spanning several discontiguous regions
2823 * (NtMapViewOfSection could then refuse to map them, and they could
2824 * e.g. only be allowed as parameters to NtCreateProcess, like on UNIX)
2825 */
2826 if ((ImageSectionObject->Segments[i - 1].Image.VirtualAddress +
2827 ImageSectionObject->Segments[i - 1].Length.QuadPart) !=
2828 ImageSectionObject->Segments[i].Image.VirtualAddress)
2829 {
2830 return FALSE;
2831 }
2832 }
2833 }
2834
2835 return TRUE;
2836 }
2837
2838 /*
2839 * Merges and pads an image section's segments until they all are page-aligned
2840 * and have a size that is a multiple of the page size
2841 */
2842 static
2843 BOOLEAN
2844 NTAPI
MmspPageAlignSegments(IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,IN ULONG Flags)2845 MmspPageAlignSegments
2846 (
2847 IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2848 IN ULONG Flags
2849 )
2850 {
2851 ULONG i;
2852 ULONG LastSegment;
2853 PMM_SECTION_SEGMENT EffectiveSegment;
2854
2855 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED)
2856 {
2857 MmspAssertSegmentsPageAligned(ImageSectionObject);
2858 return TRUE;
2859 }
2860
2861 LastSegment = 0;
2862 EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
2863
2864 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2865 {
2866 /*
2867 * The first segment requires special handling
2868 */
2869 if (i == 0)
2870 {
2871 ULONG_PTR VirtualAddress;
2872 ULONG_PTR VirtualOffset;
2873
2874 VirtualAddress = EffectiveSegment->Image.VirtualAddress;
2875
2876 /* Round down the virtual address to the nearest page */
2877 EffectiveSegment->Image.VirtualAddress = PAGE_ROUND_DOWN(VirtualAddress);
2878
2879 /* Round up the virtual size to the nearest page */
2880 EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(VirtualAddress + EffectiveSegment->Length.QuadPart) -
2881 EffectiveSegment->Image.VirtualAddress;
2882
2883 /* Adjust the raw address and size */
2884 VirtualOffset = VirtualAddress - EffectiveSegment->Image.VirtualAddress;
2885
2886 if (EffectiveSegment->Image.FileOffset < VirtualOffset)
2887 {
2888 return FALSE;
2889 }
2890
2891 /*
2892 * Garbage in, garbage out: unaligned base addresses make the file
2893 * offset point in curious and odd places, but that's what we were
2894 * asked for
2895 */
2896 EffectiveSegment->Image.FileOffset -= VirtualOffset;
2897 EffectiveSegment->RawLength.QuadPart += VirtualOffset;
2898 }
2899 else
2900 {
2901 PMM_SECTION_SEGMENT Segment = &ImageSectionObject->Segments[i];
2902 ULONG_PTR EndOfEffectiveSegment;
2903
2904 EndOfEffectiveSegment = (ULONG_PTR)(EffectiveSegment->Image.VirtualAddress + EffectiveSegment->Length.QuadPart);
2905 ASSERT((EndOfEffectiveSegment % PAGE_SIZE) == 0);
2906
2907 /*
2908 * The current segment begins exactly where the current effective
2909 * segment ended, therefore beginning a new effective segment
2910 */
2911 if (EndOfEffectiveSegment == Segment->Image.VirtualAddress)
2912 {
2913 LastSegment ++;
2914 ASSERT(LastSegment <= i);
2915 ASSERT(LastSegment < ImageSectionObject->NrSegments);
2916
2917 EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
2918
2919 if (LastSegment != i)
2920 {
2921 /*
2922 * Copy the current segment. If necessary, the effective segment
2923 * will be expanded later
2924 */
2925 *EffectiveSegment = *Segment;
2926 }
2927
2928 /*
2929 * Page-align the virtual size. We know for sure the virtual address
2930 * already is
2931 */
2932 ASSERT((EffectiveSegment->Image.VirtualAddress % PAGE_SIZE) == 0);
2933 EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(EffectiveSegment->Length.QuadPart);
2934 }
2935 /*
2936 * The current segment is still part of the current effective segment:
2937 * extend the effective segment to reflect this
2938 */
2939 else if (EndOfEffectiveSegment > Segment->Image.VirtualAddress)
2940 {
2941 static const ULONG FlagsToProtection[16] =
2942 {
2943 PAGE_NOACCESS,
2944 PAGE_READONLY,
2945 PAGE_READWRITE,
2946 PAGE_READWRITE,
2947 PAGE_EXECUTE_READ,
2948 PAGE_EXECUTE_READ,
2949 PAGE_EXECUTE_READWRITE,
2950 PAGE_EXECUTE_READWRITE,
2951 PAGE_WRITECOPY,
2952 PAGE_WRITECOPY,
2953 PAGE_WRITECOPY,
2954 PAGE_WRITECOPY,
2955 PAGE_EXECUTE_WRITECOPY,
2956 PAGE_EXECUTE_WRITECOPY,
2957 PAGE_EXECUTE_WRITECOPY,
2958 PAGE_EXECUTE_WRITECOPY
2959 };
2960
2961 unsigned ProtectionFlags;
2962
2963 /*
2964 * Extend the file size
2965 */
2966
2967 /* Unaligned segments must be contiguous within the file */
2968 if (Segment->Image.FileOffset != (EffectiveSegment->Image.FileOffset +
2969 EffectiveSegment->RawLength.QuadPart))
2970 {
2971 return FALSE;
2972 }
2973
2974 EffectiveSegment->RawLength.QuadPart += Segment->RawLength.QuadPart;
2975
2976 /*
2977 * Extend the virtual size
2978 */
2979 ASSERT(PAGE_ROUND_UP(Segment->Image.VirtualAddress + Segment->Length.QuadPart) >= EndOfEffectiveSegment);
2980
2981 EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(Segment->Image.VirtualAddress + Segment->Length.QuadPart) -
2982 EffectiveSegment->Image.VirtualAddress;
2983
2984 /*
2985 * Merge the protection
2986 */
2987 EffectiveSegment->Protection |= Segment->Protection;
2988
2989 /* Clean up redundance */
2990 ProtectionFlags = 0;
2991
2992 if(EffectiveSegment->Protection & PAGE_IS_READABLE)
2993 ProtectionFlags |= 1 << 0;
2994
2995 if(EffectiveSegment->Protection & PAGE_IS_WRITABLE)
2996 ProtectionFlags |= 1 << 1;
2997
2998 if(EffectiveSegment->Protection & PAGE_IS_EXECUTABLE)
2999 ProtectionFlags |= 1 << 2;
3000
3001 if(EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
3002 ProtectionFlags |= 1 << 3;
3003
3004 ASSERT(ProtectionFlags < 16);
3005 EffectiveSegment->Protection = FlagsToProtection[ProtectionFlags];
3006
3007 /* If a segment was required to be shared and cannot, fail */
3008 if(!(Segment->Protection & PAGE_IS_WRITECOPY) &&
3009 EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
3010 {
3011 return FALSE;
3012 }
3013 }
3014 /*
3015 * We assume no holes between segments at this point
3016 */
3017 else
3018 {
3019 KeBugCheck(MEMORY_MANAGEMENT);
3020 }
3021 }
3022 }
3023 ImageSectionObject->NrSegments = LastSegment + 1;
3024
3025 return TRUE;
3026 }
3027
3028 NTSTATUS
ExeFmtpCreateImageSection(PFILE_OBJECT FileObject,PMM_IMAGE_SECTION_OBJECT ImageSectionObject)3029 ExeFmtpCreateImageSection(PFILE_OBJECT FileObject,
3030 PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
3031 {
3032 LARGE_INTEGER Offset;
3033 PVOID FileHeader;
3034 PVOID FileHeaderBuffer;
3035 ULONG FileHeaderSize;
3036 ULONG Flags;
3037 ULONG OldNrSegments;
3038 NTSTATUS Status;
3039 ULONG i;
3040
3041 /*
3042 * Read the beginning of the file (2 pages). Should be enough to contain
3043 * all (or most) of the headers
3044 */
3045 Offset.QuadPart = 0;
3046
3047 Status = ExeFmtpReadFile (FileObject,
3048 &Offset,
3049 PAGE_SIZE * 2,
3050 &FileHeader,
3051 &FileHeaderBuffer,
3052 &FileHeaderSize);
3053
3054 if (!NT_SUCCESS(Status))
3055 return Status;
3056
3057 if (FileHeaderSize == 0)
3058 {
3059 ExFreePool(FileHeaderBuffer);
3060 return STATUS_UNSUCCESSFUL;
3061 }
3062
3063 /*
3064 * Look for a loader that can handle this executable
3065 */
3066 for (i = 0; i < RTL_NUMBER_OF(ExeFmtpLoaders); ++ i)
3067 {
3068 Flags = 0;
3069
3070 Status = ExeFmtpLoaders[i](FileHeader,
3071 FileHeaderSize,
3072 FileObject,
3073 ImageSectionObject,
3074 &Flags,
3075 ExeFmtpReadFile,
3076 ExeFmtpAllocateSegments);
3077
3078 if (!NT_SUCCESS(Status))
3079 {
3080 if (ImageSectionObject->Segments)
3081 {
3082 ExFreePool(ImageSectionObject->Segments);
3083 ImageSectionObject->Segments = NULL;
3084 }
3085 }
3086
3087 if (Status != STATUS_ROS_EXEFMT_UNKNOWN_FORMAT)
3088 break;
3089 }
3090
3091 ExFreePoolWithTag(FileHeaderBuffer, 'rXmM');
3092
3093 /*
3094 * No loader handled the format
3095 */
3096 if (Status == STATUS_ROS_EXEFMT_UNKNOWN_FORMAT)
3097 {
3098 Status = STATUS_INVALID_IMAGE_NOT_MZ;
3099 ASSERT(!NT_SUCCESS(Status));
3100 }
3101
3102 if (!NT_SUCCESS(Status))
3103 return Status;
3104
3105 ASSERT(ImageSectionObject->Segments != NULL);
3106 ASSERT(ImageSectionObject->RefCount > 0);
3107
3108 /*
3109 * Some defaults
3110 */
3111 /* FIXME? are these values platform-dependent? */
3112 if (ImageSectionObject->ImageInformation.MaximumStackSize == 0)
3113 ImageSectionObject->ImageInformation.MaximumStackSize = 0x40000;
3114
3115 if(ImageSectionObject->ImageInformation.CommittedStackSize == 0)
3116 ImageSectionObject->ImageInformation.CommittedStackSize = 0x1000;
3117
3118 if(ImageSectionObject->BasedAddress == NULL)
3119 {
3120 if(ImageSectionObject->ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL)
3121 ImageSectionObject->BasedAddress = (PVOID)0x10000000;
3122 else
3123 ImageSectionObject->BasedAddress = (PVOID)0x00400000;
3124 }
3125
3126 /*
3127 * And now the fun part: fixing the segments
3128 */
3129
3130 /* Sort them by virtual address */
3131 MmspSortSegments(ImageSectionObject, Flags);
3132
3133 /* Ensure they don't overlap in memory */
3134 if (!MmspCheckSegmentBounds(ImageSectionObject, Flags))
3135 return STATUS_INVALID_IMAGE_FORMAT;
3136
3137 /* Ensure they are aligned */
3138 OldNrSegments = ImageSectionObject->NrSegments;
3139
3140 if (!MmspPageAlignSegments(ImageSectionObject, Flags))
3141 return STATUS_INVALID_IMAGE_FORMAT;
3142
3143 /* Trim them if the alignment phase merged some of them */
3144 if (ImageSectionObject->NrSegments < OldNrSegments)
3145 {
3146 PMM_SECTION_SEGMENT Segments;
3147 SIZE_T SizeOfSegments;
3148
3149 SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * ImageSectionObject->NrSegments;
3150
3151 Segments = ExAllocatePoolWithTag(PagedPool,
3152 SizeOfSegments,
3153 TAG_MM_SECTION_SEGMENT);
3154
3155 if (Segments == NULL)
3156 return STATUS_INSUFFICIENT_RESOURCES;
3157
3158 RtlCopyMemory(Segments, ImageSectionObject->Segments, SizeOfSegments);
3159 ExFreePool(ImageSectionObject->Segments);
3160 ImageSectionObject->Segments = Segments;
3161 }
3162
3163 /* And finish their initialization */
3164 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3165 {
3166 ExInitializeFastMutex(&ImageSectionObject->Segments[i].Lock);
3167 ImageSectionObject->Segments[i].ReferenceCount = &ImageSectionObject->RefCount;
3168 ImageSectionObject->Segments[i].Flags = &ImageSectionObject->SegFlags;
3169 MiInitializeSectionPageTable(&ImageSectionObject->Segments[i]);
3170 ImageSectionObject->Segments[i].FileObject = FileObject;
3171 }
3172
3173 ASSERT(ImageSectionObject->RefCount > 0);
3174
3175 ImageSectionObject->FileObject = FileObject;
3176
3177 ASSERT(NT_SUCCESS(Status));
3178 return Status;
3179 }
3180
3181 NTSTATUS
MmCreateImageSection(PSECTION * SectionObject,ACCESS_MASK DesiredAccess,POBJECT_ATTRIBUTES ObjectAttributes,PLARGE_INTEGER UMaximumSize,ULONG SectionPageProtection,ULONG AllocationAttributes,PFILE_OBJECT FileObject)3182 MmCreateImageSection(PSECTION *SectionObject,
3183 ACCESS_MASK DesiredAccess,
3184 POBJECT_ATTRIBUTES ObjectAttributes,
3185 PLARGE_INTEGER UMaximumSize,
3186 ULONG SectionPageProtection,
3187 ULONG AllocationAttributes,
3188 PFILE_OBJECT FileObject)
3189 {
3190 PSECTION Section;
3191 NTSTATUS Status;
3192 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3193 KIRQL OldIrql;
3194
3195
3196 if (FileObject == NULL)
3197 return STATUS_INVALID_FILE_FOR_SECTION;
3198
3199 if (FileObject->SectionObjectPointer == NULL)
3200 {
3201 DPRINT1("Denying section creation due to missing cache initialization\n");
3202 return STATUS_INVALID_FILE_FOR_SECTION;
3203 }
3204
3205 /*
3206 * Create the section
3207 */
3208 Status = ObCreateObject (ExGetPreviousMode(),
3209 MmSectionObjectType,
3210 ObjectAttributes,
3211 ExGetPreviousMode(),
3212 NULL,
3213 sizeof(*Section),
3214 0,
3215 0,
3216 (PVOID*)(PVOID)&Section);
3217 if (!NT_SUCCESS(Status))
3218 {
3219 return Status;
3220 }
3221
3222 /*
3223 * Initialize it
3224 */
3225 RtlZeroMemory(Section, sizeof(*Section));
3226
3227 /* Mark this as a "ROS" Section */
3228 Section->u.Flags.filler = 1;
3229
3230 Section->InitialPageProtection = SectionPageProtection;
3231 Section->u.Flags.File = 1;
3232 Section->u.Flags.Image = 1;
3233 if (AllocationAttributes & SEC_NO_CHANGE)
3234 Section->u.Flags.NoChange = 1;
3235
3236 grab_image_section_object:
3237 OldIrql = MiAcquirePfnLock();
3238
3239 /* Wait for it to be properly created or deleted */
3240 ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
3241 while(ImageSectionObject && (ImageSectionObject->SegFlags & (MM_SEGMENT_INDELETE | MM_SEGMENT_INCREATE)))
3242 {
3243 MiReleasePfnLock(OldIrql);
3244
3245 KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
3246
3247 OldIrql = MiAcquirePfnLock();
3248 ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
3249 }
3250
3251 if (ImageSectionObject == NULL)
3252 {
3253 NTSTATUS StatusExeFmt;
3254
3255 /* Release the lock because ExAllocatePoolWithTag could need to acquire it */
3256 MiReleasePfnLock(OldIrql);
3257
3258 ImageSectionObject = ExAllocatePoolZero(NonPagedPool, sizeof(MM_IMAGE_SECTION_OBJECT), TAG_MM_SECTION_SEGMENT);
3259 if (ImageSectionObject == NULL)
3260 {
3261 ObDereferenceObject(Section);
3262 return STATUS_NO_MEMORY;
3263 }
3264
3265 ImageSectionObject->SegFlags = MM_SEGMENT_INCREATE;
3266 ImageSectionObject->RefCount = 1;
3267 ImageSectionObject->SectionCount = 1;
3268
3269 OldIrql = MiAcquirePfnLock();
3270 if (FileObject->SectionObjectPointer->ImageSectionObject != NULL)
3271 {
3272 MiReleasePfnLock(OldIrql);
3273 /* Bad luck. Start over */
3274 ExFreePoolWithTag(ImageSectionObject, TAG_MM_SECTION_SEGMENT);
3275 goto grab_image_section_object;
3276 }
3277
3278 FileObject->SectionObjectPointer->ImageSectionObject = ImageSectionObject;
3279
3280 MiReleasePfnLock(OldIrql);
3281
3282 /* Purge the cache */
3283 CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, NULL);
3284
3285 StatusExeFmt = ExeFmtpCreateImageSection(FileObject, ImageSectionObject);
3286
3287 if (!NT_SUCCESS(StatusExeFmt))
3288 {
3289 /* Unset */
3290 OldIrql = MiAcquirePfnLock();
3291 FileObject->SectionObjectPointer->ImageSectionObject = NULL;
3292 MiReleasePfnLock(OldIrql);
3293
3294 if(ImageSectionObject->Segments != NULL)
3295 ExFreePool(ImageSectionObject->Segments);
3296
3297 /*
3298 * If image file is empty, then return that the file is invalid for section
3299 */
3300 Status = StatusExeFmt;
3301 if (StatusExeFmt == STATUS_END_OF_FILE)
3302 {
3303 Status = STATUS_INVALID_FILE_FOR_SECTION;
3304 }
3305
3306 ExFreePoolWithTag(ImageSectionObject, TAG_MM_SECTION_SEGMENT);
3307 ObDereferenceObject(Section);
3308 return Status;
3309 }
3310
3311 Section->Segment = (PSEGMENT)ImageSectionObject;
3312 ASSERT(ImageSectionObject->Segments);
3313 ASSERT(ImageSectionObject->RefCount > 0);
3314
3315 /*
3316 * Lock the file
3317 */
3318 Status = MmspWaitForFileLock(FileObject);
3319 if (!NT_SUCCESS(Status))
3320 {
3321 /* Unset */
3322 OldIrql = MiAcquirePfnLock();
3323 FileObject->SectionObjectPointer->ImageSectionObject = NULL;
3324 MiReleasePfnLock(OldIrql);
3325
3326 ExFreePool(ImageSectionObject->Segments);
3327 ExFreePool(ImageSectionObject);
3328 ObDereferenceObject(Section);
3329 return Status;
3330 }
3331
3332 OldIrql = MiAcquirePfnLock();
3333 ImageSectionObject->SegFlags &= ~MM_SEGMENT_INCREATE;
3334
3335 /* Take a ref on the file on behalf of the newly created structure */
3336 ObReferenceObject(FileObject);
3337
3338 MiReleasePfnLock(OldIrql);
3339
3340 Status = StatusExeFmt;
3341 }
3342 else
3343 {
3344 /* If FS driver called for delete, tell them it's not possible anymore. */
3345 ImageSectionObject->SegFlags &= ~MM_IMAGE_SECTION_FLUSH_DELETE;
3346
3347 /* Take one ref */
3348 InterlockedIncrement64(&ImageSectionObject->RefCount);
3349 ImageSectionObject->SectionCount++;
3350
3351 MiReleasePfnLock(OldIrql);
3352
3353 Section->Segment = (PSEGMENT)ImageSectionObject;
3354
3355 Status = STATUS_SUCCESS;
3356 }
3357 //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
3358 *SectionObject = Section;
3359 ASSERT(ImageSectionObject->RefCount > 0);
3360
3361 return Status;
3362 }
3363
3364
3365
3366 static NTSTATUS
MmMapViewOfSegment(PMMSUPPORT AddressSpace,BOOLEAN AsImage,PMM_SECTION_SEGMENT Segment,PVOID * BaseAddress,SIZE_T ViewSize,ULONG Protect,LONGLONG ViewOffset,ULONG AllocationType)3367 MmMapViewOfSegment(
3368 PMMSUPPORT AddressSpace,
3369 BOOLEAN AsImage,
3370 PMM_SECTION_SEGMENT Segment,
3371 PVOID* BaseAddress,
3372 SIZE_T ViewSize,
3373 ULONG Protect,
3374 LONGLONG ViewOffset,
3375 ULONG AllocationType)
3376 {
3377 PMEMORY_AREA MArea;
3378 NTSTATUS Status;
3379 ULONG Granularity;
3380
3381 ASSERT(ViewSize != 0);
3382
3383 if (Segment->WriteCopy)
3384 {
3385 /* We have to do this because the not present fault
3386 * and access fault handlers depend on the protection
3387 * that should be granted AFTER the COW fault takes
3388 * place to be in Region->Protect. The not present fault
3389 * handler changes this to the correct protection for COW when
3390 * mapping the pages into the process's address space. If a COW
3391 * fault takes place, the access fault handler sets the page protection
3392 * to these values for the newly copied pages
3393 */
3394 if (Protect == PAGE_WRITECOPY)
3395 Protect = PAGE_READWRITE;
3396 else if (Protect == PAGE_EXECUTE_WRITECOPY)
3397 Protect = PAGE_EXECUTE_READWRITE;
3398 }
3399
3400 if (*BaseAddress == NULL)
3401 Granularity = MM_ALLOCATION_GRANULARITY;
3402 else
3403 Granularity = PAGE_SIZE;
3404
3405 #ifdef NEWCC
3406 if (Segment->Flags & MM_DATAFILE_SEGMENT)
3407 {
3408 LARGE_INTEGER FileOffset;
3409 FileOffset.QuadPart = ViewOffset;
3410 ObReferenceObject(Section);
3411 return _MiMapViewOfSegment(AddressSpace, Segment, BaseAddress, ViewSize, Protect, &FileOffset, AllocationType, __FILE__, __LINE__);
3412 }
3413 #endif
3414 Status = MmCreateMemoryArea(AddressSpace,
3415 MEMORY_AREA_SECTION_VIEW,
3416 BaseAddress,
3417 ViewSize,
3418 Protect,
3419 &MArea,
3420 AllocationType,
3421 Granularity);
3422 if (!NT_SUCCESS(Status))
3423 {
3424 DPRINT1("Mapping between 0x%p and 0x%p failed (%X).\n",
3425 (*BaseAddress), (char*)(*BaseAddress) + ViewSize, Status);
3426 return Status;
3427 }
3428
3429 InterlockedIncrement64(Segment->ReferenceCount);
3430
3431 MArea->SectionData.Segment = Segment;
3432 MArea->SectionData.ViewOffset = ViewOffset;
3433 if (AsImage)
3434 {
3435 MArea->VadNode.u.VadFlags.VadType = VadImageMap;
3436 }
3437
3438 MmInitializeRegion(&MArea->SectionData.RegionListHead,
3439 ViewSize, 0, Protect);
3440
3441 return STATUS_SUCCESS;
3442 }
3443
3444
3445 static VOID
MmFreeSectionPage(PVOID Context,MEMORY_AREA * MemoryArea,PVOID Address,PFN_NUMBER Page,SWAPENTRY SwapEntry,BOOLEAN Dirty)3446 MmFreeSectionPage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address,
3447 PFN_NUMBER Page, SWAPENTRY SwapEntry, BOOLEAN Dirty)
3448 {
3449 ULONG_PTR Entry;
3450 LARGE_INTEGER Offset;
3451 SWAPENTRY SavedSwapEntry;
3452 PMM_SECTION_SEGMENT Segment;
3453 PMMSUPPORT AddressSpace;
3454 PEPROCESS Process;
3455
3456 AddressSpace = (PMMSUPPORT)Context;
3457 Process = MmGetAddressSpaceOwner(AddressSpace);
3458
3459 Address = (PVOID)PAGE_ROUND_DOWN(Address);
3460
3461 Offset.QuadPart = ((ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea)) +
3462 MemoryArea->SectionData.ViewOffset;
3463
3464 Segment = MemoryArea->SectionData.Segment;
3465
3466 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
3467 while (Entry && MM_IS_WAIT_PTE(Entry))
3468 {
3469 MmUnlockSectionSegment(Segment);
3470 MmUnlockAddressSpace(AddressSpace);
3471
3472 KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
3473
3474 MmLockAddressSpace(AddressSpace);
3475 MmLockSectionSegment(Segment);
3476 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
3477 }
3478
3479 /*
3480 * For a dirty, datafile, non-private page, there shoulkd be no swap entry
3481 */
3482 if (*Segment->Flags & MM_DATAFILE_SEGMENT)
3483 {
3484 if (Page == PFN_FROM_SSE(Entry) && Dirty)
3485 {
3486 ASSERT(SwapEntry == 0);
3487 }
3488 }
3489
3490 if (SwapEntry != 0)
3491 {
3492 /*
3493 * Sanity check
3494 */
3495 MmFreeSwapPage(SwapEntry);
3496 }
3497 else if (Page != 0)
3498 {
3499 if (IS_SWAP_FROM_SSE(Entry) ||
3500 Page != PFN_FROM_SSE(Entry))
3501 {
3502 ASSERT(Process != NULL);
3503
3504 /*
3505 * Just dereference private pages
3506 */
3507 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
3508 if (SavedSwapEntry != 0)
3509 {
3510 MmFreeSwapPage(SavedSwapEntry);
3511 MmSetSavedSwapEntryPage(Page, 0);
3512 }
3513 MmDeleteRmap(Page, Process, Address);
3514 MmReleasePageMemoryConsumer(MC_USER, Page);
3515 }
3516 else
3517 {
3518 if (Process)
3519 {
3520 MmDeleteRmap(Page, Process, Address);
3521 }
3522
3523 /* We don't dirtify for System Space Maps. We let Cc manage that */
3524 MmUnsharePageEntrySectionSegment(MemoryArea, Segment, &Offset, Process ? Dirty : FALSE, FALSE, NULL);
3525 }
3526 }
3527 }
3528
3529 static NTSTATUS
MmUnmapViewOfSegment(PMMSUPPORT AddressSpace,PVOID BaseAddress)3530 MmUnmapViewOfSegment(PMMSUPPORT AddressSpace,
3531 PVOID BaseAddress)
3532 {
3533 NTSTATUS Status;
3534 PMEMORY_AREA MemoryArea;
3535 PMM_SECTION_SEGMENT Segment;
3536 PLIST_ENTRY CurrentEntry;
3537 PMM_REGION CurrentRegion;
3538 PLIST_ENTRY RegionListHead;
3539
3540 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace,
3541 BaseAddress);
3542 if (MemoryArea == NULL)
3543 {
3544 return STATUS_UNSUCCESSFUL;
3545 }
3546
3547 Segment = MemoryArea->SectionData.Segment;
3548
3549 #ifdef NEWCC
3550 if (Segment->Flags & MM_DATAFILE_SEGMENT)
3551 {
3552 MmUnlockAddressSpace(AddressSpace);
3553 Status = MmUnmapViewOfCacheSegment(AddressSpace, BaseAddress);
3554 MmLockAddressSpace(AddressSpace);
3555
3556 return Status;
3557 }
3558 #endif
3559
3560 MemoryArea->DeleteInProgress = TRUE;
3561
3562 MmLockSectionSegment(Segment);
3563
3564 RegionListHead = &MemoryArea->SectionData.RegionListHead;
3565 while (!IsListEmpty(RegionListHead))
3566 {
3567 CurrentEntry = RemoveHeadList(RegionListHead);
3568 CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION, RegionListEntry);
3569 ExFreePoolWithTag(CurrentRegion, TAG_MM_REGION);
3570 }
3571
3572 if ((*Segment->Flags) & MM_PHYSICALMEMORY_SEGMENT)
3573 {
3574 Status = MmFreeMemoryArea(AddressSpace,
3575 MemoryArea,
3576 NULL,
3577 NULL);
3578 }
3579 else
3580 {
3581 Status = MmFreeMemoryArea(AddressSpace,
3582 MemoryArea,
3583 MmFreeSectionPage,
3584 AddressSpace);
3585 }
3586 MmUnlockSectionSegment(Segment);
3587 MmDereferenceSegment(Segment);
3588 return Status;
3589 }
3590
3591 /* This functions must be called with a locked address space */
3592 NTSTATUS
3593 NTAPI
MiRosUnmapViewOfSection(IN PEPROCESS Process,IN PVOID BaseAddress,IN BOOLEAN SkipDebuggerNotify)3594 MiRosUnmapViewOfSection(IN PEPROCESS Process,
3595 IN PVOID BaseAddress,
3596 IN BOOLEAN SkipDebuggerNotify)
3597 {
3598 NTSTATUS Status;
3599 PMEMORY_AREA MemoryArea;
3600 PMMSUPPORT AddressSpace;
3601 PVOID ImageBaseAddress = 0;
3602
3603 DPRINT("Opening memory area Process %p BaseAddress %p\n",
3604 Process, BaseAddress);
3605
3606 ASSERT(Process);
3607
3608 AddressSpace = &Process->Vm;
3609
3610 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace,
3611 BaseAddress);
3612 if (MemoryArea == NULL ||
3613 #ifdef NEWCC
3614 ((MemoryArea->Type != MEMORY_AREA_SECTION_VIEW) && (MemoryArea->Type != MEMORY_AREA_CACHE)) ||
3615 #else
3616 (MemoryArea->Type != MEMORY_AREA_SECTION_VIEW) ||
3617 #endif
3618 MemoryArea->DeleteInProgress)
3619
3620 {
3621 if (MemoryArea) ASSERT(MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3);
3622
3623 DPRINT1("Unable to find memory area at address %p.\n", BaseAddress);
3624 return STATUS_NOT_MAPPED_VIEW;
3625 }
3626
3627 if (MemoryArea->VadNode.u.VadFlags.VadType == VadImageMap)
3628 {
3629 ULONG i;
3630 ULONG NrSegments;
3631 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3632 PMM_SECTION_SEGMENT SectionSegments;
3633 PMM_SECTION_SEGMENT Segment;
3634
3635 Segment = MemoryArea->SectionData.Segment;
3636 ImageSectionObject = ImageSectionObjectFromSegment(Segment);
3637 SectionSegments = ImageSectionObject->Segments;
3638 NrSegments = ImageSectionObject->NrSegments;
3639
3640 MemoryArea->DeleteInProgress = TRUE;
3641
3642 /* Search for the current segment within the section segments
3643 * and calculate the image base address */
3644 for (i = 0; i < NrSegments; i++)
3645 {
3646 if (Segment == &SectionSegments[i])
3647 {
3648 ImageBaseAddress = (char*)BaseAddress - (ULONG_PTR)SectionSegments[i].Image.VirtualAddress;
3649 break;
3650 }
3651 }
3652 if (i >= NrSegments)
3653 {
3654 KeBugCheck(MEMORY_MANAGEMENT);
3655 }
3656
3657 for (i = 0; i < NrSegments; i++)
3658 {
3659 PVOID SBaseAddress = (PVOID)
3660 ((char*)ImageBaseAddress + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
3661
3662 Status = MmUnmapViewOfSegment(AddressSpace, SBaseAddress);
3663 if (!NT_SUCCESS(Status))
3664 {
3665 DPRINT1("MmUnmapViewOfSegment failed for %p (Process %p) with %lx\n",
3666 SBaseAddress, Process, Status);
3667 ASSERT(NT_SUCCESS(Status));
3668 }
3669 }
3670 DPRINT("One mapping less for %p\n", ImageSectionObject->FileObject->SectionObjectPointer);
3671 InterlockedDecrement(&ImageSectionObject->MapCount);
3672 }
3673 else
3674 {
3675 PMM_SECTION_SEGMENT Segment = MemoryArea->SectionData.Segment;
3676 PMMVAD Vad = &MemoryArea->VadNode;
3677 PCONTROL_AREA ControlArea = Vad->ControlArea;
3678 PFILE_OBJECT FileObject;
3679 SIZE_T ViewSize;
3680 LARGE_INTEGER ViewOffset;
3681 ViewOffset.QuadPart = MemoryArea->SectionData.ViewOffset;
3682
3683 InterlockedIncrement64(Segment->ReferenceCount);
3684
3685 ViewSize = PAGE_SIZE + ((Vad->EndingVpn - Vad->StartingVpn) << PAGE_SHIFT);
3686
3687 Status = MmUnmapViewOfSegment(AddressSpace, BaseAddress);
3688 if (!NT_SUCCESS(Status))
3689 {
3690 DPRINT1("MmUnmapViewOfSegment failed for %p (Process %p) with %lx\n",
3691 BaseAddress, Process, Status);
3692 ASSERT(NT_SUCCESS(Status));
3693 }
3694
3695 /* These might be deleted now */
3696 Vad = NULL;
3697 MemoryArea = NULL;
3698
3699 if (FlagOn(*Segment->Flags, MM_PHYSICALMEMORY_SEGMENT))
3700 {
3701 /* Don't bother */
3702 MmDereferenceSegment(Segment);
3703 return STATUS_SUCCESS;
3704 }
3705 ASSERT(FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT));
3706
3707 FileObject = Segment->FileObject;
3708 FsRtlAcquireFileExclusive(FileObject);
3709
3710 /* Don't bother for auto-delete closed file. */
3711 if (FlagOn(FileObject->Flags, FO_DELETE_ON_CLOSE) && FlagOn(FileObject->Flags, FO_CLEANUP_COMPLETE))
3712 {
3713 FsRtlReleaseFile(FileObject);
3714 MmDereferenceSegment(Segment);
3715 return STATUS_SUCCESS;
3716 }
3717
3718 /*
3719 * Flush only when last mapping is deleted.
3720 * FIXME: Why ControlArea == NULL? Or rather: is ControlArea ever not NULL here?
3721 */
3722 if (ControlArea == NULL || ControlArea->NumberOfMappedViews == 1)
3723 {
3724 while (ViewSize > 0)
3725 {
3726 ULONG FlushSize = min(ViewSize, PAGE_ROUND_DOWN(MAXULONG));
3727 MmFlushSegment(FileObject->SectionObjectPointer,
3728 &ViewOffset,
3729 FlushSize,
3730 NULL);
3731 ViewSize -= FlushSize;
3732 ViewOffset.QuadPart += FlushSize;
3733 }
3734 }
3735
3736 FsRtlReleaseFile(FileObject);
3737 MmDereferenceSegment(Segment);
3738 }
3739
3740 /* Notify debugger */
3741 if (ImageBaseAddress && !SkipDebuggerNotify) DbgkUnMapViewOfSection(ImageBaseAddress);
3742
3743 return STATUS_SUCCESS;
3744 }
3745
3746
3747
3748
3749 /**
3750 * Queries the information of a section object.
3751 *
3752 * @param SectionHandle
3753 * Handle to the section object. It must be opened with SECTION_QUERY
3754 * access.
3755 * @param SectionInformationClass
3756 * Index to a certain information structure. Can be either
3757 * SectionBasicInformation or SectionImageInformation. The latter
3758 * is valid only for sections that were created with the SEC_IMAGE
3759 * flag.
3760 * @param SectionInformation
3761 * Caller supplies storage for resulting information.
3762 * @param Length
3763 * Size of the supplied storage.
3764 * @param ResultLength
3765 * Data written.
3766 *
3767 * @return Status.
3768 *
3769 * @implemented
3770 */
3771 NTSTATUS
3772 NTAPI
NtQuerySection(_In_ HANDLE SectionHandle,_In_ SECTION_INFORMATION_CLASS SectionInformationClass,_Out_ PVOID SectionInformation,_In_ SIZE_T SectionInformationLength,_Out_opt_ PSIZE_T ResultLength)3773 NtQuerySection(
3774 _In_ HANDLE SectionHandle,
3775 _In_ SECTION_INFORMATION_CLASS SectionInformationClass,
3776 _Out_ PVOID SectionInformation,
3777 _In_ SIZE_T SectionInformationLength,
3778 _Out_opt_ PSIZE_T ResultLength)
3779 {
3780 PSECTION Section;
3781 KPROCESSOR_MODE PreviousMode;
3782 NTSTATUS Status;
3783 PAGED_CODE();
3784
3785 PreviousMode = ExGetPreviousMode();
3786 if (PreviousMode != KernelMode)
3787 {
3788 _SEH2_TRY
3789 {
3790 ProbeForWrite(SectionInformation,
3791 SectionInformationLength,
3792 __alignof(ULONG));
3793 if (ResultLength != NULL)
3794 {
3795 ProbeForWrite(ResultLength,
3796 sizeof(*ResultLength),
3797 __alignof(SIZE_T));
3798 }
3799 }
3800 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3801 {
3802 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3803 }
3804 _SEH2_END;
3805 }
3806
3807 if (SectionInformationClass == SectionBasicInformation)
3808 {
3809 if (SectionInformationLength < sizeof(SECTION_BASIC_INFORMATION))
3810 {
3811 return STATUS_INFO_LENGTH_MISMATCH;
3812 }
3813 }
3814 else if (SectionInformationClass == SectionImageInformation)
3815 {
3816 if (SectionInformationLength < sizeof(SECTION_IMAGE_INFORMATION))
3817 {
3818 return STATUS_INFO_LENGTH_MISMATCH;
3819 }
3820 }
3821 else
3822 {
3823 return STATUS_INVALID_INFO_CLASS;
3824 }
3825
3826 Status = ObReferenceObjectByHandle(SectionHandle,
3827 SECTION_QUERY,
3828 MmSectionObjectType,
3829 PreviousMode,
3830 (PVOID*)(PVOID)&Section,
3831 NULL);
3832 if (!NT_SUCCESS(Status))
3833 {
3834 DPRINT1("Failed to reference section: 0x%lx\n", Status);
3835 return Status;
3836 }
3837
3838 switch(SectionInformationClass)
3839 {
3840 case SectionBasicInformation:
3841 {
3842 SECTION_BASIC_INFORMATION Sbi;
3843
3844 Sbi.Size = Section->SizeOfSection;
3845 Sbi.BaseAddress = (PVOID)Section->Address.StartingVpn;
3846
3847 Sbi.Attributes = 0;
3848 if (Section->u.Flags.File)
3849 Sbi.Attributes |= SEC_FILE;
3850 if (Section->u.Flags.Image)
3851 Sbi.Attributes |= SEC_IMAGE;
3852
3853 /* Those are not set *************
3854 if (Section->u.Flags.Commit)
3855 Sbi.Attributes |= SEC_COMMIT;
3856 if (Section->u.Flags.Reserve)
3857 Sbi.Attributes |= SEC_RESERVE;
3858 **********************************/
3859
3860 if (Section->u.Flags.Image)
3861 {
3862 if (MiIsRosSectionObject(Section))
3863 {
3864 PMM_IMAGE_SECTION_OBJECT ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
3865 Sbi.BaseAddress = 0;
3866 Sbi.Size.QuadPart = ImageSectionObject->ImageInformation.ImageFileSize;
3867 }
3868 else
3869 {
3870 /* Not supported yet */
3871 ASSERT(FALSE);
3872 }
3873 }
3874 else if (MiIsRosSectionObject(Section))
3875 {
3876 Sbi.BaseAddress = (PVOID)((PMM_SECTION_SEGMENT)Section->Segment)->Image.VirtualAddress;
3877 Sbi.Size.QuadPart = ((PMM_SECTION_SEGMENT)Section->Segment)->RawLength.QuadPart;
3878 }
3879 else
3880 {
3881 DPRINT1("Unimplemented code path\n");
3882 }
3883
3884 _SEH2_TRY
3885 {
3886 *((SECTION_BASIC_INFORMATION*)SectionInformation) = Sbi;
3887 if (ResultLength != NULL)
3888 {
3889 *ResultLength = sizeof(Sbi);
3890 }
3891 }
3892 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3893 {
3894 Status = _SEH2_GetExceptionCode();
3895 }
3896 _SEH2_END;
3897 break;
3898 }
3899 case SectionImageInformation:
3900 {
3901 if (!Section->u.Flags.Image)
3902 {
3903 Status = STATUS_SECTION_NOT_IMAGE;
3904 }
3905 else if (MiIsRosSectionObject(Section))
3906 {
3907 PMM_IMAGE_SECTION_OBJECT ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
3908
3909 _SEH2_TRY
3910 {
3911 PSECTION_IMAGE_INFORMATION Sii = (PSECTION_IMAGE_INFORMATION)SectionInformation;
3912 *Sii = ImageSectionObject->ImageInformation;
3913 if (ResultLength != NULL)
3914 {
3915 *ResultLength = sizeof(*Sii);
3916 }
3917 }
3918 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3919 {
3920 Status = _SEH2_GetExceptionCode();
3921 }
3922 _SEH2_END;
3923 }
3924 else
3925 {
3926 _SEH2_TRY
3927 {
3928 PSECTION_IMAGE_INFORMATION Sii = (PSECTION_IMAGE_INFORMATION)SectionInformation;
3929 *Sii = *Section->Segment->u2.ImageInformation;
3930 if (ResultLength != NULL)
3931 *ResultLength = sizeof(*Sii);
3932 }
3933 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3934 {
3935 Status = _SEH2_GetExceptionCode();
3936 }
3937 _SEH2_END;
3938 }
3939 break;
3940 }
3941 default:
3942 DPRINT1("Unknown SectionInformationClass: %d\n", SectionInformationClass);
3943 Status = STATUS_NOT_SUPPORTED;
3944 }
3945
3946 ObDereferenceObject(Section);
3947
3948 return Status;
3949 }
3950
3951 /**********************************************************************
3952 * NAME EXPORTED
3953 * MmMapViewOfSection
3954 *
3955 * DESCRIPTION
3956 * Maps a view of a section into the virtual address space of a
3957 * process.
3958 *
3959 * ARGUMENTS
3960 * Section
3961 * Pointer to the section object.
3962 *
3963 * ProcessHandle
3964 * Pointer to the process.
3965 *
3966 * BaseAddress
3967 * Desired base address (or NULL) on entry;
3968 * Actual base address of the view on exit.
3969 *
3970 * ZeroBits
3971 * Number of high order address bits that must be zero.
3972 *
3973 * CommitSize
3974 * Size in bytes of the initially committed section of
3975 * the view.
3976 *
3977 * SectionOffset
3978 * Offset in bytes from the beginning of the section
3979 * to the beginning of the view.
3980 *
3981 * ViewSize
3982 * Desired length of map (or zero to map all) on entry
3983 * Actual length mapped on exit.
3984 *
3985 * InheritDisposition
3986 * Specified how the view is to be shared with
3987 * child processes.
3988 *
3989 * AllocationType
3990 * Type of allocation for the pages.
3991 *
3992 * Protect
3993 * Protection for the committed region of the view.
3994 *
3995 * RETURN VALUE
3996 * Status.
3997 *
3998 * @implemented
3999 */
4000 NTSTATUS NTAPI
MmMapViewOfSection(IN PVOID SectionObject,IN PEPROCESS Process,IN OUT PVOID * BaseAddress,IN ULONG_PTR ZeroBits,IN SIZE_T CommitSize,IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,IN OUT PSIZE_T ViewSize,IN SECTION_INHERIT InheritDisposition,IN ULONG AllocationType,IN ULONG Protect)4001 MmMapViewOfSection(IN PVOID SectionObject,
4002 IN PEPROCESS Process,
4003 IN OUT PVOID *BaseAddress,
4004 IN ULONG_PTR ZeroBits,
4005 IN SIZE_T CommitSize,
4006 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
4007 IN OUT PSIZE_T ViewSize,
4008 IN SECTION_INHERIT InheritDisposition,
4009 IN ULONG AllocationType,
4010 IN ULONG Protect)
4011 {
4012 PSECTION Section;
4013 PMMSUPPORT AddressSpace;
4014 NTSTATUS Status = STATUS_SUCCESS;
4015 BOOLEAN NotAtBase = FALSE;
4016 BOOLEAN IsAttached = FALSE;
4017 KAPC_STATE ApcState;
4018
4019 if (MiIsRosSectionObject(SectionObject) == FALSE)
4020 {
4021 DPRINT("Mapping ARM3 section into %s\n", Process->ImageFileName);
4022 return MmMapViewOfArm3Section(SectionObject,
4023 Process,
4024 BaseAddress,
4025 ZeroBits,
4026 CommitSize,
4027 SectionOffset,
4028 ViewSize,
4029 InheritDisposition,
4030 AllocationType,
4031 Protect);
4032 }
4033
4034 ASSERT(Process);
4035
4036 if (!Protect || Protect & ~PAGE_FLAGS_VALID_FOR_SECTION)
4037 {
4038 return STATUS_INVALID_PAGE_PROTECTION;
4039 }
4040
4041 if (PsGetCurrentProcess() != Process)
4042 {
4043 KeStackAttachProcess(&Process->Pcb, &ApcState);
4044 IsAttached = TRUE;
4045 }
4046
4047 /* FIXME: We should keep this, but it would break code checking equality */
4048 Protect &= ~PAGE_NOCACHE;
4049
4050 Section = SectionObject;
4051 AddressSpace = &Process->Vm;
4052
4053 if (Section->u.Flags.NoChange)
4054 AllocationType |= SEC_NO_CHANGE;
4055
4056 MmLockAddressSpace(AddressSpace);
4057
4058 if (Section->u.Flags.Image)
4059 {
4060 ULONG i;
4061 ULONG NrSegments;
4062 ULONG_PTR ImageBase;
4063 SIZE_T ImageSize;
4064 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
4065 PMM_SECTION_SEGMENT SectionSegments;
4066
4067 ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
4068 SectionSegments = ImageSectionObject->Segments;
4069 NrSegments = ImageSectionObject->NrSegments;
4070
4071 ASSERT(ImageSectionObject->RefCount > 0);
4072
4073 ImageBase = (ULONG_PTR)*BaseAddress;
4074 if (ImageBase == 0)
4075 {
4076 ImageBase = (ULONG_PTR)ImageSectionObject->BasedAddress;
4077 }
4078
4079 ImageSize = 0;
4080 for (i = 0; i < NrSegments; i++)
4081 {
4082 ULONG_PTR MaxExtent;
4083 MaxExtent = (ULONG_PTR)(SectionSegments[i].Image.VirtualAddress +
4084 SectionSegments[i].Length.QuadPart);
4085 ImageSize = max(ImageSize, MaxExtent);
4086 }
4087
4088 ImageSectionObject->ImageInformation.ImageFileSize = (ULONG)ImageSize;
4089
4090 /* Check for an illegal base address */
4091 if (((ImageBase + ImageSize) > (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS) ||
4092 ((ImageBase + ImageSize) < ImageSize))
4093 {
4094 ASSERT(*BaseAddress == NULL);
4095 ImageBase = ALIGN_DOWN_BY((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - ImageSize,
4096 MM_VIRTMEM_GRANULARITY);
4097 NotAtBase = TRUE;
4098 }
4099 else if (ImageBase != ALIGN_DOWN_BY(ImageBase, MM_VIRTMEM_GRANULARITY))
4100 {
4101 ASSERT(*BaseAddress == NULL);
4102 ImageBase = ALIGN_DOWN_BY(ImageBase, MM_VIRTMEM_GRANULARITY);
4103 NotAtBase = TRUE;
4104 }
4105
4106 /* Check there is enough space to map the section at that point. */
4107 if (MmLocateMemoryAreaByRegion(AddressSpace, (PVOID)ImageBase,
4108 PAGE_ROUND_UP(ImageSize)) != NULL)
4109 {
4110 /* Fail if the user requested a fixed base address. */
4111 if ((*BaseAddress) != NULL)
4112 {
4113 Status = STATUS_CONFLICTING_ADDRESSES;
4114 goto Exit;
4115 }
4116 /* Otherwise find a gap to map the image. */
4117 ImageBase = (ULONG_PTR)MmFindGap(AddressSpace, PAGE_ROUND_UP(ImageSize), MM_VIRTMEM_GRANULARITY, FALSE);
4118 if (ImageBase == 0)
4119 {
4120 Status = STATUS_CONFLICTING_ADDRESSES;
4121 goto Exit;
4122 }
4123 /* Remember that we loaded image at a different base address */
4124 NotAtBase = TRUE;
4125 }
4126
4127 for (i = 0; i < NrSegments; i++)
4128 {
4129 PVOID SBaseAddress = (PVOID)
4130 ((char*)ImageBase + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
4131 MmLockSectionSegment(&SectionSegments[i]);
4132 Status = MmMapViewOfSegment(AddressSpace,
4133 TRUE,
4134 &SectionSegments[i],
4135 &SBaseAddress,
4136 SectionSegments[i].Length.QuadPart,
4137 SectionSegments[i].Protection,
4138 0,
4139 0);
4140 MmUnlockSectionSegment(&SectionSegments[i]);
4141 if (!NT_SUCCESS(Status))
4142 {
4143 /* roll-back */
4144 while (i--)
4145 {
4146 SBaseAddress = ((char*)ImageBase + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
4147 MmLockSectionSegment(&SectionSegments[i]);
4148 MmUnmapViewOfSegment(AddressSpace, SBaseAddress);
4149 MmUnlockSectionSegment(&SectionSegments[i]);
4150 }
4151
4152 goto Exit;
4153 }
4154 }
4155
4156 *BaseAddress = (PVOID)ImageBase;
4157 *ViewSize = ImageSize;
4158
4159 DPRINT("Mapped %p for section pointer %p\n", ImageSectionObject, ImageSectionObject->FileObject->SectionObjectPointer);
4160
4161 /* One more map */
4162 InterlockedIncrement(&ImageSectionObject->MapCount);
4163 }
4164 else
4165 {
4166 PMM_SECTION_SEGMENT Segment = (PMM_SECTION_SEGMENT)Section->Segment;
4167 LONGLONG ViewOffset;
4168
4169 ASSERT(Segment->RefCount > 0);
4170
4171 /* check for write access */
4172 if ((Protect & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE)) &&
4173 !(Section->InitialPageProtection & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE)))
4174 {
4175 Status = STATUS_SECTION_PROTECTION;
4176 goto Exit;
4177 }
4178 /* check for read access */
4179 if ((Protect & (PAGE_READONLY|PAGE_WRITECOPY|PAGE_EXECUTE_READ|PAGE_EXECUTE_WRITECOPY)) &&
4180 !(Section->InitialPageProtection & (PAGE_READONLY|PAGE_READWRITE|PAGE_WRITECOPY|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY)))
4181 {
4182 Status = STATUS_SECTION_PROTECTION;
4183 goto Exit;
4184 }
4185 /* check for execute access */
4186 if ((Protect & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY)) &&
4187 !(Section->InitialPageProtection & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY)))
4188 {
4189 Status = STATUS_SECTION_PROTECTION;
4190 goto Exit;
4191 }
4192
4193 if (SectionOffset == NULL)
4194 {
4195 ViewOffset = 0;
4196 }
4197 else
4198 {
4199 ViewOffset = SectionOffset->QuadPart;
4200 }
4201
4202 if ((ViewOffset % PAGE_SIZE) != 0)
4203 {
4204 Status = STATUS_MAPPED_ALIGNMENT;
4205 goto Exit;
4206 }
4207
4208 if ((*ViewSize) == 0)
4209 {
4210 (*ViewSize) = Section->SizeOfSection.QuadPart - ViewOffset;
4211 }
4212 else if ((ExGetPreviousMode() == UserMode) &&
4213 (((*ViewSize)+ViewOffset) > Section->SizeOfSection.QuadPart) &&
4214 (!Section->u.Flags.Reserve))
4215 {
4216 /* Dubious */
4217 (*ViewSize) = MIN(Section->SizeOfSection.QuadPart - ViewOffset, SIZE_T_MAX - PAGE_SIZE);
4218 }
4219
4220 *ViewSize = PAGE_ROUND_UP(*ViewSize);
4221
4222 MmLockSectionSegment(Segment);
4223 Status = MmMapViewOfSegment(AddressSpace,
4224 FALSE,
4225 Segment,
4226 BaseAddress,
4227 *ViewSize,
4228 Protect,
4229 ViewOffset,
4230 AllocationType & (MEM_TOP_DOWN|SEC_NO_CHANGE));
4231 MmUnlockSectionSegment(Segment);
4232 if (!NT_SUCCESS(Status))
4233 {
4234 goto Exit;
4235 }
4236 }
4237
4238 if (NotAtBase)
4239 Status = STATUS_IMAGE_NOT_AT_BASE;
4240 else
4241 Status = STATUS_SUCCESS;
4242
4243 Exit:
4244
4245 MmUnlockAddressSpace(AddressSpace);
4246
4247 if (IsAttached)
4248 {
4249 KeUnstackDetachProcess(&ApcState);
4250 }
4251
4252 return Status;
4253 }
4254
4255 /*
4256 * @unimplemented
4257 */
4258 BOOLEAN
4259 NTAPI
MmCanFileBeTruncated(_In_ PSECTION_OBJECT_POINTERS SectionObjectPointer,_In_opt_ PLARGE_INTEGER NewFileSize)4260 MmCanFileBeTruncated(
4261 _In_ PSECTION_OBJECT_POINTERS SectionObjectPointer,
4262 _In_opt_ PLARGE_INTEGER NewFileSize)
4263 {
4264 BOOLEAN Ret;
4265 PMM_SECTION_SEGMENT Segment;
4266
4267 /* Check whether an ImageSectionObject exists */
4268 if (SectionObjectPointer->ImageSectionObject != NULL)
4269 {
4270 DPRINT1("ERROR: File can't be truncated because it has an image section\n");
4271 return FALSE;
4272 }
4273
4274 Segment = MiGrabDataSection(SectionObjectPointer);
4275 if (!Segment)
4276 {
4277 /* There is no data section. It's fine to do anything. */
4278 return TRUE;
4279 }
4280
4281 MmLockSectionSegment(Segment);
4282 if ((Segment->SectionCount == 0) ||
4283 ((Segment->SectionCount == 1) && (SectionObjectPointer->SharedCacheMap != NULL)))
4284 {
4285 /* If the cache is the only one holding a reference to the segment, then it's fine to resize */
4286 Ret = TRUE;
4287 }
4288 else if (NewFileSize != NULL)
4289 {
4290 /* We can't shrink, but we can extend */
4291 Ret = NewFileSize->QuadPart >= Segment->RawLength.QuadPart;
4292 #if DBG
4293 if (!Ret)
4294 {
4295 DPRINT1("Cannot truncate data: New Size %I64d, Segment Size %I64d\n", NewFileSize->QuadPart, Segment->RawLength.QuadPart);
4296 }
4297 #endif
4298 }
4299 else
4300 {
4301 DPRINT1("ERROR: File can't be truncated because it has references held to its data section\n");
4302 Ret = FALSE;
4303 }
4304
4305 MmUnlockSectionSegment(Segment);
4306 MmDereferenceSegment(Segment);
4307
4308 DPRINT("FIXME: didn't check for outstanding write probes\n");
4309
4310 return Ret;
4311 }
4312
4313 static
4314 BOOLEAN
MiPurgeImageSegment(PMM_SECTION_SEGMENT Segment)4315 MiPurgeImageSegment(PMM_SECTION_SEGMENT Segment)
4316 {
4317 PCACHE_SECTION_PAGE_TABLE PageTable;
4318
4319 MmLockSectionSegment(Segment);
4320
4321 /* Loop over all entries */
4322 for (PageTable = RtlEnumerateGenericTable(&Segment->PageTable, TRUE);
4323 PageTable != NULL;
4324 PageTable = RtlEnumerateGenericTable(&Segment->PageTable, FALSE))
4325 {
4326 for (ULONG i = 0; i < _countof(PageTable->PageEntries); i++)
4327 {
4328 ULONG_PTR Entry = PageTable->PageEntries[i];
4329 LARGE_INTEGER Offset;
4330
4331 if (!Entry)
4332 continue;
4333
4334 if (IS_SWAP_FROM_SSE(Entry) || (SHARE_COUNT_FROM_SSE(Entry) > 0))
4335 {
4336 /* I/O ongoing or swap entry. Someone mapped this file as we were not looking */
4337 MmUnlockSectionSegment(Segment);
4338 return FALSE;
4339 }
4340
4341 /* Regular entry */
4342 ASSERT(!IS_WRITE_SSE(Entry));
4343 ASSERT(MmGetSavedSwapEntryPage(PFN_FROM_SSE(Entry)) == 0);
4344
4345 /* Properly remove using the used API */
4346 Offset.QuadPart = PageTable->FileOffset.QuadPart + (i << PAGE_SHIFT);
4347 MmSetPageEntrySectionSegment(Segment, &Offset, 0);
4348 MmReleasePageMemoryConsumer(MC_USER, PFN_FROM_SSE(Entry));
4349 }
4350 }
4351
4352 MmUnlockSectionSegment(Segment);
4353
4354 return TRUE;
4355 }
4356
4357 /*
4358 * @implemented
4359 */
4360 BOOLEAN NTAPI
MmFlushImageSection(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,IN MMFLUSH_TYPE FlushType)4361 MmFlushImageSection (IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
4362 IN MMFLUSH_TYPE FlushType)
4363 {
4364 switch(FlushType)
4365 {
4366 case MmFlushForDelete:
4367 {
4368 /*
4369 * FIXME: Check for outstanding write probes on Data section.
4370 * How do we do that ?
4371 */
4372 }
4373 /* Fall-through */
4374 case MmFlushForWrite:
4375 {
4376 KIRQL OldIrql = MiAcquirePfnLock();
4377 PMM_IMAGE_SECTION_OBJECT ImageSectionObject = SectionObjectPointer->ImageSectionObject;
4378
4379 DPRINT("Deleting or modifying %p\n", SectionObjectPointer);
4380
4381 /* Wait for concurrent creation or deletion of image to be done */
4382 ImageSectionObject = SectionObjectPointer->ImageSectionObject;
4383 while (ImageSectionObject && (ImageSectionObject->SegFlags & (MM_SEGMENT_INCREATE | MM_SEGMENT_INDELETE)))
4384 {
4385 MiReleasePfnLock(OldIrql);
4386 KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
4387 OldIrql = MiAcquirePfnLock();
4388 ImageSectionObject = SectionObjectPointer->ImageSectionObject;
4389 }
4390
4391 if (!ImageSectionObject)
4392 {
4393 DPRINT("No image section object. Accepting\n");
4394 /* Nothing to do */
4395 MiReleasePfnLock(OldIrql);
4396 return TRUE;
4397 }
4398
4399 /* Do we have open sections or mappings on it ? */
4400 if ((ImageSectionObject->SectionCount) || (ImageSectionObject->MapCount))
4401 {
4402 /* We do. No way to delete it */
4403 MiReleasePfnLock(OldIrql);
4404 DPRINT("Denying. There are mappings open\n");
4405 return FALSE;
4406 }
4407
4408 /* There are no sections open on it, but we must still have pages around. Discard everything */
4409 ImageSectionObject->SegFlags |= MM_IMAGE_SECTION_FLUSH_DELETE;
4410 InterlockedIncrement64(&ImageSectionObject->RefCount);
4411 MiReleasePfnLock(OldIrql);
4412
4413 DPRINT("Purging\n");
4414
4415 for (ULONG i = 0; i < ImageSectionObject->NrSegments; i++)
4416 {
4417 if (!MiPurgeImageSegment(&ImageSectionObject->Segments[i]))
4418 break;
4419 }
4420
4421 /* Grab lock again */
4422 OldIrql = MiAcquirePfnLock();
4423
4424 if (!(ImageSectionObject->SegFlags & MM_IMAGE_SECTION_FLUSH_DELETE))
4425 {
4426 /*
4427 * Someone actually created a section while we were not looking.
4428 * Drop our ref and deny.
4429 * MmDereferenceSegmentWithLock releases Pfn lock
4430 */
4431 MmDereferenceSegmentWithLock(&ImageSectionObject->Segments[0], OldIrql);
4432 return FALSE;
4433 }
4434
4435 /* We should be the last one holding a ref here. */
4436 ASSERT(ImageSectionObject->RefCount == 1);
4437 ASSERT(ImageSectionObject->SectionCount == 0);
4438
4439 /* Dereference the first segment, this will free everything & release the lock */
4440 MmDereferenceSegmentWithLock(&ImageSectionObject->Segments[0], OldIrql);
4441 return TRUE;
4442 }
4443 }
4444 return FALSE;
4445 }
4446
4447 /*
4448 * @implemented
4449 */
4450 NTSTATUS
4451 NTAPI
MmMapViewInSystemSpace(IN PVOID SectionObject,OUT PVOID * MappedBase,IN OUT PSIZE_T ViewSize)4452 MmMapViewInSystemSpace (IN PVOID SectionObject,
4453 OUT PVOID * MappedBase,
4454 IN OUT PSIZE_T ViewSize)
4455 {
4456 LARGE_INTEGER SectionOffset;
4457
4458 SectionOffset.QuadPart = 0;
4459
4460 return MmMapViewInSystemSpaceEx(SectionObject, MappedBase, ViewSize, &SectionOffset, 0);
4461 }
4462
4463 NTSTATUS
4464 NTAPI
MmMapViewInSystemSpaceEx(_In_ PVOID SectionObject,_Outptr_result_bytebuffer_ (* ViewSize)PVOID * MappedBase,_Inout_ PSIZE_T ViewSize,_Inout_ PLARGE_INTEGER SectionOffset,_In_ ULONG_PTR Flags)4465 MmMapViewInSystemSpaceEx (
4466 _In_ PVOID SectionObject,
4467 _Outptr_result_bytebuffer_ (*ViewSize) PVOID *MappedBase,
4468 _Inout_ PSIZE_T ViewSize,
4469 _Inout_ PLARGE_INTEGER SectionOffset,
4470 _In_ ULONG_PTR Flags
4471 )
4472 {
4473 PSECTION Section = SectionObject;
4474 PMM_SECTION_SEGMENT Segment;
4475 PMMSUPPORT AddressSpace;
4476 NTSTATUS Status;
4477
4478 UNREFERENCED_PARAMETER(Flags);
4479
4480 PAGED_CODE();
4481
4482 if (MiIsRosSectionObject(SectionObject) == FALSE)
4483 {
4484 return MiMapViewInSystemSpace(SectionObject,
4485 &MmSession,
4486 MappedBase,
4487 ViewSize,
4488 SectionOffset);
4489 }
4490
4491 DPRINT("MmMapViewInSystemSpaceEx() called\n");
4492
4493 /* unsupported for now */
4494 ASSERT(Section->u.Flags.Image == 0);
4495
4496 Section = SectionObject;
4497 Segment = (PMM_SECTION_SEGMENT)Section->Segment;
4498
4499 if (*ViewSize == 0)
4500 {
4501 LONGLONG MapSizeLL;
4502
4503 /* Page-align the mapping */
4504 SectionOffset->LowPart = PAGE_ROUND_DOWN(SectionOffset->LowPart);
4505
4506 if (!NT_SUCCESS(RtlLongLongSub(Section->SizeOfSection.QuadPart, SectionOffset->QuadPart, &MapSizeLL)))
4507 return STATUS_INVALID_VIEW_SIZE;
4508
4509 if (!NT_SUCCESS(RtlLongLongToSIZET(MapSizeLL, ViewSize)))
4510 return STATUS_INVALID_VIEW_SIZE;
4511 }
4512 else
4513 {
4514 LONGLONG HelperLL;
4515
4516 /* Get the map end */
4517 if (!NT_SUCCESS(RtlLongLongAdd(SectionOffset->QuadPart, *ViewSize, &HelperLL)))
4518 return STATUS_INVALID_VIEW_SIZE;
4519
4520 /* Round it up, if needed */
4521 if (HelperLL % PAGE_SIZE)
4522 {
4523 if (!NT_SUCCESS(RtlLongLongAdd(HelperLL, PAGE_SIZE - (HelperLL % PAGE_SIZE), &HelperLL)))
4524 return STATUS_INVALID_VIEW_SIZE;
4525 }
4526
4527 /* Now that we have the mapping end, we can align down its start */
4528 SectionOffset->LowPart = PAGE_ROUND_DOWN(SectionOffset->LowPart);
4529
4530 /* Get the new size */
4531 if (!NT_SUCCESS(RtlLongLongSub(HelperLL, SectionOffset->QuadPart, &HelperLL)))
4532 return STATUS_INVALID_VIEW_SIZE;
4533
4534 if (!NT_SUCCESS(RtlLongLongToSIZET(HelperLL, ViewSize)))
4535 return STATUS_INVALID_VIEW_SIZE;
4536 }
4537
4538 AddressSpace = MmGetKernelAddressSpace();
4539
4540 MmLockAddressSpace(AddressSpace);
4541
4542 MmLockSectionSegment(Segment);
4543
4544 Status = MmMapViewOfSegment(AddressSpace,
4545 Section->u.Flags.Image,
4546 Segment,
4547 MappedBase,
4548 *ViewSize,
4549 PAGE_READWRITE,
4550 SectionOffset->QuadPart,
4551 SEC_RESERVE);
4552
4553 MmUnlockSectionSegment(Segment);
4554 MmUnlockAddressSpace(AddressSpace);
4555
4556 return Status;
4557 }
4558
4559 /* This function must be called with address space lock held */
4560 NTSTATUS
4561 NTAPI
MiRosUnmapViewInSystemSpace(IN PVOID MappedBase)4562 MiRosUnmapViewInSystemSpace(IN PVOID MappedBase)
4563 {
4564 DPRINT("MmUnmapViewInSystemSpace() called\n");
4565
4566 return MmUnmapViewOfSegment(MmGetKernelAddressSpace(), MappedBase);
4567 }
4568
4569 /**********************************************************************
4570 * NAME EXPORTED
4571 * MmCreateSection@
4572 *
4573 * DESCRIPTION
4574 * Creates a section object.
4575 *
4576 * ARGUMENTS
4577 * SectionObject (OUT)
4578 * Caller supplied storage for the resulting pointer
4579 * to a SECTION_OBJECT instance;
4580 *
4581 * DesiredAccess
4582 * Specifies the desired access to the section can be a
4583 * combination of:
4584 * STANDARD_RIGHTS_REQUIRED |
4585 * SECTION_QUERY |
4586 * SECTION_MAP_WRITE |
4587 * SECTION_MAP_READ |
4588 * SECTION_MAP_EXECUTE
4589 *
4590 * ObjectAttributes [OPTIONAL]
4591 * Initialized attributes for the object can be used
4592 * to create a named section;
4593 *
4594 * MaximumSize
4595 * Maximizes the size of the memory section. Must be
4596 * non-NULL for a page-file backed section.
4597 * If value specified for a mapped file and the file is
4598 * not large enough, file will be extended.
4599 *
4600 * SectionPageProtection
4601 * Can be a combination of:
4602 * PAGE_READONLY |
4603 * PAGE_READWRITE |
4604 * PAGE_WRITEONLY |
4605 * PAGE_WRITECOPY
4606 *
4607 * AllocationAttributes
4608 * Can be a combination of:
4609 * SEC_IMAGE |
4610 * SEC_RESERVE
4611 *
4612 * FileHandle
4613 * Handle to a file to create a section mapped to a file
4614 * instead of a memory backed section;
4615 *
4616 * File
4617 * Unknown.
4618 *
4619 * RETURN VALUE
4620 * Status.
4621 *
4622 * @implemented
4623 */
4624 NTSTATUS NTAPI
MmCreateSection(OUT PVOID * Section,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,IN PLARGE_INTEGER MaximumSize,IN ULONG SectionPageProtection,IN ULONG AllocationAttributes,IN HANDLE FileHandle OPTIONAL,IN PFILE_OBJECT FileObject OPTIONAL)4625 MmCreateSection (OUT PVOID * Section,
4626 IN ACCESS_MASK DesiredAccess,
4627 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
4628 IN PLARGE_INTEGER MaximumSize,
4629 IN ULONG SectionPageProtection,
4630 IN ULONG AllocationAttributes,
4631 IN HANDLE FileHandle OPTIONAL,
4632 IN PFILE_OBJECT FileObject OPTIONAL)
4633 {
4634 NTSTATUS Status;
4635 ULONG Protection;
4636 PSECTION *SectionObject = (PSECTION *)Section;
4637 BOOLEAN FileLock = FALSE;
4638
4639 /* Check if an ARM3 section is being created instead */
4640 if (!(AllocationAttributes & (SEC_IMAGE | SEC_PHYSICALMEMORY)))
4641 {
4642 if (!(FileObject) && !(FileHandle))
4643 {
4644 return MmCreateArm3Section(Section,
4645 DesiredAccess,
4646 ObjectAttributes,
4647 MaximumSize,
4648 SectionPageProtection,
4649 AllocationAttributes &~ 1,
4650 FileHandle,
4651 FileObject);
4652 }
4653 }
4654
4655 /* Convert section flag to page flag */
4656 if (AllocationAttributes & SEC_NOCACHE) SectionPageProtection |= PAGE_NOCACHE;
4657
4658 /* Check to make sure the protection is correct. Nt* does this already */
4659 Protection = MiMakeProtectionMask(SectionPageProtection);
4660 if (Protection == MM_INVALID_PROTECTION)
4661 {
4662 DPRINT1("Page protection is invalid\n");
4663 return STATUS_INVALID_PAGE_PROTECTION;
4664 }
4665
4666 /* Check if this is going to be a data or image backed file section */
4667 if ((FileHandle) || (FileObject))
4668 {
4669 /* These cannot be mapped with large pages */
4670 if (AllocationAttributes & SEC_LARGE_PAGES)
4671 {
4672 DPRINT1("Large pages cannot be used with an image mapping\n");
4673 return STATUS_INVALID_PARAMETER_6;
4674 }
4675
4676 /* Did the caller pass a file object ? */
4677 if (FileObject)
4678 {
4679 /* Reference the object directly */
4680 ObReferenceObject(FileObject);
4681
4682 /* We don't create image mappings with file objects */
4683 AllocationAttributes &= ~SEC_IMAGE;
4684 }
4685 else
4686 {
4687 /* Reference the file handle to get the object */
4688 Status = ObReferenceObjectByHandle(FileHandle,
4689 MmMakeFileAccess[Protection],
4690 IoFileObjectType,
4691 ExGetPreviousMode(),
4692 (PVOID*)&FileObject,
4693 NULL);
4694 if (!NT_SUCCESS(Status))
4695 {
4696 DPRINT1("Failed to get a handle to the FO: %lx\n", Status);
4697 return Status;
4698 }
4699
4700 /* Lock the file */
4701 Status = FsRtlAcquireToCreateMappedSection(FileObject, SectionPageProtection);
4702 if (!NT_SUCCESS(Status))
4703 {
4704 ObDereferenceObject(FileObject);
4705 return Status;
4706 }
4707
4708 FileLock = TRUE;
4709
4710 /* Deny access if there are writes on the file */
4711 #if 0
4712 if ((AllocationAttributes & SEC_IMAGE) && (Status == STATUS_FILE_LOCKED_WITH_WRITERS))
4713 {
4714 DPRINT1("Cannot create image maps with writers open on the file!\n");
4715 Status = STATUS_ACCESS_DENIED;
4716 goto Quit;
4717 }
4718 #else
4719 if ((AllocationAttributes & SEC_IMAGE) && (Status == STATUS_FILE_LOCKED_WITH_WRITERS))
4720 DPRINT1("Creating image map with writers open on the file!\n");
4721 #endif
4722 }
4723 }
4724 else
4725 {
4726 /* A handle must be supplied with SEC_IMAGE, as this is the no-handle path */
4727 if (AllocationAttributes & SEC_IMAGE) return STATUS_INVALID_FILE_FOR_SECTION;
4728 }
4729
4730 if (AllocationAttributes & SEC_IMAGE)
4731 {
4732 Status = MmCreateImageSection(SectionObject,
4733 DesiredAccess,
4734 ObjectAttributes,
4735 MaximumSize,
4736 SectionPageProtection,
4737 AllocationAttributes,
4738 FileObject);
4739 }
4740 #ifndef NEWCC
4741 else if (FileObject != NULL)
4742 {
4743 Status = MmCreateDataFileSection(SectionObject,
4744 DesiredAccess,
4745 ObjectAttributes,
4746 MaximumSize,
4747 SectionPageProtection,
4748 AllocationAttributes,
4749 FileObject,
4750 FileHandle != NULL);
4751 }
4752 #else
4753 else if (FileHandle != NULL || FileObject != NULL)
4754 {
4755 Status = MmCreateCacheSection(SectionObject,
4756 DesiredAccess,
4757 ObjectAttributes,
4758 MaximumSize,
4759 SectionPageProtection,
4760 AllocationAttributes,
4761 FileObject);
4762 }
4763 #endif
4764 else
4765 {
4766 /* All cases should be handled above */
4767 Status = STATUS_INVALID_PARAMETER;
4768 }
4769
4770 if (FileLock)
4771 FsRtlReleaseFile(FileObject);
4772 if (FileObject)
4773 ObDereferenceObject(FileObject);
4774
4775 return Status;
4776 }
4777
4778 BOOLEAN
4779 NTAPI
MmArePagesResident(_In_ PEPROCESS Process,_In_ PVOID Address,_In_ ULONG Length)4780 MmArePagesResident(
4781 _In_ PEPROCESS Process,
4782 _In_ PVOID Address,
4783 _In_ ULONG Length)
4784 {
4785 PMEMORY_AREA MemoryArea;
4786 BOOLEAN Ret = TRUE;
4787 PMM_SECTION_SEGMENT Segment;
4788 LARGE_INTEGER SegmentOffset, RangeEnd;
4789 PMMSUPPORT AddressSpace = Process ? &Process->Vm : MmGetKernelAddressSpace();
4790
4791 MmLockAddressSpace(AddressSpace);
4792
4793 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
4794 if (MemoryArea == NULL)
4795 {
4796 MmUnlockAddressSpace(AddressSpace);
4797 return FALSE;
4798 }
4799
4800 /* Only supported in old Mm for now */
4801 ASSERT(MemoryArea->Type == MEMORY_AREA_SECTION_VIEW);
4802 /* For file mappings */
4803 ASSERT(MemoryArea->VadNode.u.VadFlags.VadType != VadImageMap);
4804
4805 Segment = MemoryArea->SectionData.Segment;
4806 MmLockSectionSegment(Segment);
4807
4808 SegmentOffset.QuadPart = PAGE_ROUND_DOWN(Address) - MA_GetStartingAddress(MemoryArea)
4809 + MemoryArea->SectionData.ViewOffset;
4810 RangeEnd.QuadPart = PAGE_ROUND_UP((ULONG_PTR)Address + Length) - MA_GetStartingAddress(MemoryArea)
4811 + MemoryArea->SectionData.ViewOffset;
4812
4813 while (SegmentOffset.QuadPart < RangeEnd.QuadPart)
4814 {
4815 ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &SegmentOffset);
4816 if ((Entry == 0) || IS_SWAP_FROM_SSE(Entry))
4817 {
4818 Ret = FALSE;
4819 break;
4820 }
4821 SegmentOffset.QuadPart += PAGE_SIZE;
4822 }
4823
4824 MmUnlockSectionSegment(Segment);
4825
4826 MmUnlockAddressSpace(AddressSpace);
4827 return Ret;
4828 }
4829
4830 /* Like CcPurgeCache but for the in-memory segment */
4831 BOOLEAN
4832 NTAPI
MmPurgeSegment(_In_ PSECTION_OBJECT_POINTERS SectionObjectPointer,_In_opt_ PLARGE_INTEGER Offset,_In_ ULONG Length)4833 MmPurgeSegment(
4834 _In_ PSECTION_OBJECT_POINTERS SectionObjectPointer,
4835 _In_opt_ PLARGE_INTEGER Offset,
4836 _In_ ULONG Length)
4837 {
4838 LARGE_INTEGER PurgeStart, PurgeEnd;
4839 PMM_SECTION_SEGMENT Segment;
4840
4841 Segment = MiGrabDataSection(SectionObjectPointer);
4842 if (!Segment)
4843 {
4844 /* Nothing to purge */
4845 return TRUE;
4846 }
4847
4848 PurgeStart.QuadPart = Offset ? Offset->QuadPart : 0LL;
4849 if (Length && Offset)
4850 {
4851 if (!NT_SUCCESS(RtlLongLongAdd(PurgeStart.QuadPart, Length, &PurgeEnd.QuadPart)))
4852 return FALSE;
4853 }
4854
4855 MmLockSectionSegment(Segment);
4856
4857 if (!Length || !Offset)
4858 {
4859 /* We must calculate the length for ourselves */
4860 /* FIXME: All of this is suboptimal */
4861 ULONG ElemCount = RtlNumberGenericTableElements(&Segment->PageTable);
4862 /* No page. Nothing to purge */
4863 if (!ElemCount)
4864 {
4865 MmUnlockSectionSegment(Segment);
4866 MmDereferenceSegment(Segment);
4867 return TRUE;
4868 }
4869
4870 PCACHE_SECTION_PAGE_TABLE PageTable = RtlGetElementGenericTable(&Segment->PageTable, ElemCount - 1);
4871 PurgeEnd.QuadPart = PageTable->FileOffset.QuadPart + _countof(PageTable->PageEntries) * PAGE_SIZE;
4872 }
4873
4874 while (PurgeStart.QuadPart < PurgeEnd.QuadPart)
4875 {
4876 ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &PurgeStart);
4877
4878 if (Entry == 0)
4879 {
4880 PurgeStart.QuadPart += PAGE_SIZE;
4881 continue;
4882 }
4883
4884 if (IS_SWAP_FROM_SSE(Entry))
4885 {
4886 ASSERT(SWAPENTRY_FROM_SSE(Entry) == MM_WAIT_ENTRY);
4887 /* The page is currently being read. Meaning someone will need it soon. Bad luck */
4888 MmUnlockSectionSegment(Segment);
4889 MmDereferenceSegment(Segment);
4890 return FALSE;
4891 }
4892
4893 if (IS_WRITE_SSE(Entry))
4894 {
4895 /* We're trying to purge an entry which is being written. Restart this loop iteration */
4896 MmUnlockSectionSegment(Segment);
4897 KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
4898 MmLockSectionSegment(Segment);
4899 continue;
4900 }
4901
4902 if (SHARE_COUNT_FROM_SSE(Entry) > 0)
4903 {
4904 /* This page is currently in use. Bad luck */
4905 MmUnlockSectionSegment(Segment);
4906 MmDereferenceSegment(Segment);
4907 return FALSE;
4908 }
4909
4910 /* We can let this page go */
4911 MmSetPageEntrySectionSegment(Segment, &PurgeStart, 0);
4912 MmReleasePageMemoryConsumer(MC_USER, PFN_FROM_SSE(Entry));
4913
4914 PurgeStart.QuadPart += PAGE_SIZE;
4915 }
4916
4917 /* This page is currently in use. Bad luck */
4918 MmUnlockSectionSegment(Segment);
4919 MmDereferenceSegment(Segment);
4920 return TRUE;
4921 }
4922
4923 NTSTATUS
4924 NTAPI
MmMakeDataSectionResident(_In_ PSECTION_OBJECT_POINTERS SectionObjectPointer,_In_ LONGLONG Offset,_In_ ULONG Length,_In_ PLARGE_INTEGER ValidDataLength)4925 MmMakeDataSectionResident(
4926 _In_ PSECTION_OBJECT_POINTERS SectionObjectPointer,
4927 _In_ LONGLONG Offset,
4928 _In_ ULONG Length,
4929 _In_ PLARGE_INTEGER ValidDataLength)
4930 {
4931 PMM_SECTION_SEGMENT Segment = MiGrabDataSection(SectionObjectPointer);
4932
4933 /* There must be a segment for this call */
4934 ASSERT(Segment);
4935
4936 NTSTATUS Status = MmMakeSegmentResident(Segment, Offset, Length, ValidDataLength, FALSE);
4937
4938 MmDereferenceSegment(Segment);
4939
4940 return Status;
4941 }
4942
4943 NTSTATUS
4944 NTAPI
MmFlushSegment(_In_ PSECTION_OBJECT_POINTERS SectionObjectPointer,_In_opt_ PLARGE_INTEGER Offset,_In_ ULONG Length,_Out_opt_ PIO_STATUS_BLOCK Iosb)4945 MmFlushSegment(
4946 _In_ PSECTION_OBJECT_POINTERS SectionObjectPointer,
4947 _In_opt_ PLARGE_INTEGER Offset,
4948 _In_ ULONG Length,
4949 _Out_opt_ PIO_STATUS_BLOCK Iosb)
4950 {
4951 LARGE_INTEGER FlushStart, FlushEnd;
4952 NTSTATUS Status;
4953
4954 if (Offset)
4955 {
4956 FlushStart = *Offset;
4957 Status = RtlLongLongAdd(FlushStart.QuadPart, Length, &FlushEnd.QuadPart);
4958 if (!NT_SUCCESS(Status))
4959 return Status;
4960 }
4961
4962 if (Iosb)
4963 Iosb->Information = 0;
4964
4965 PMM_SECTION_SEGMENT Segment = MiGrabDataSection(SectionObjectPointer);
4966 if (!Segment)
4967 {
4968 /* Nothing to flush */
4969 goto Quit;
4970 }
4971
4972 ASSERT(*Segment->Flags & MM_DATAFILE_SEGMENT);
4973
4974 MmLockSectionSegment(Segment);
4975
4976 if (!Offset)
4977 {
4978 FlushStart.QuadPart = 0;
4979
4980 /* FIXME: All of this is suboptimal */
4981 ULONG ElemCount = RtlNumberGenericTableElements(&Segment->PageTable);
4982 if (!ElemCount)
4983 {
4984 /* No page. Nothing to flush */
4985 MmUnlockSectionSegment(Segment);
4986 MmDereferenceSegment(Segment);
4987 goto Quit;
4988 }
4989
4990 PCACHE_SECTION_PAGE_TABLE PageTable = RtlGetElementGenericTable(&Segment->PageTable, ElemCount - 1);
4991 FlushEnd.QuadPart = PageTable->FileOffset.QuadPart + _countof(PageTable->PageEntries) * PAGE_SIZE;
4992 }
4993
4994 FlushStart.QuadPart >>= PAGE_SHIFT;
4995 FlushStart.QuadPart <<= PAGE_SHIFT;
4996
4997 while (FlushStart.QuadPart < FlushEnd.QuadPart)
4998 {
4999 ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &FlushStart);
5000
5001 if (IS_DIRTY_SSE(Entry))
5002 {
5003 MmCheckDirtySegment(Segment, &FlushStart, FALSE, FALSE);
5004
5005 if (Iosb)
5006 Iosb->Information += PAGE_SIZE;
5007 }
5008
5009 FlushStart.QuadPart += PAGE_SIZE;
5010 }
5011
5012 MmUnlockSectionSegment(Segment);
5013 MmDereferenceSegment(Segment);
5014
5015 Quit:
5016 /* FIXME: Handle failures */
5017 if (Iosb)
5018 Iosb->Status = STATUS_SUCCESS;
5019
5020 return STATUS_SUCCESS;
5021 }
5022
5023 _Requires_exclusive_lock_held_(Segment->Lock)
5024 BOOLEAN
5025 NTAPI
MmCheckDirtySegment(PMM_SECTION_SEGMENT Segment,PLARGE_INTEGER Offset,BOOLEAN ForceDirty,BOOLEAN PageOut)5026 MmCheckDirtySegment(
5027 PMM_SECTION_SEGMENT Segment,
5028 PLARGE_INTEGER Offset,
5029 BOOLEAN ForceDirty,
5030 BOOLEAN PageOut)
5031 {
5032 ULONG_PTR Entry;
5033 NTSTATUS Status;
5034 PFN_NUMBER Page;
5035
5036 ASSERT(Segment->Locked);
5037
5038 ASSERT((Offset->QuadPart % PAGE_SIZE) == 0);
5039
5040 DPRINT("Checking segment for file %wZ at offset 0x%I64X.\n", &Segment->FileObject->FileName, Offset->QuadPart);
5041
5042 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
5043 if (Entry == 0)
5044 return FALSE;
5045
5046 Page = PFN_FROM_SSE(Entry);
5047 if ((IS_DIRTY_SSE(Entry)) || ForceDirty)
5048 {
5049 BOOLEAN DirtyAgain;
5050
5051 /*
5052 * We got a dirty entry. This path is for the shared data,
5053 * be-it regular file maps or shared sections of DLLs
5054 */
5055 ASSERT(FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT) ||
5056 FlagOn(Segment->Image.Characteristics, IMAGE_SCN_MEM_SHARED));
5057
5058 /* Insert the cleaned entry back. Mark it as write in progress, and clear the dirty bit. */
5059 Entry = MAKE_SSE(PAGE_FROM_SSE(Entry), SHARE_COUNT_FROM_SSE(Entry) + 1);
5060 Entry = WRITE_SSE(Entry);
5061 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
5062
5063 MmUnlockSectionSegment(Segment);
5064
5065 if (FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT))
5066 {
5067 PERESOURCE ResourceToRelease = NULL;
5068 KIRQL OldIrql;
5069
5070 /* We have to write it back to the file. Tell the FS driver who we are */
5071 if (PageOut)
5072 {
5073 LARGE_INTEGER EndOffset = *Offset;
5074
5075 ASSERT(IoGetTopLevelIrp() == NULL);
5076
5077 /* We need to disable all APCs */
5078 KeRaiseIrql(APC_LEVEL, &OldIrql);
5079
5080 EndOffset.QuadPart += PAGE_SIZE;
5081 Status = FsRtlAcquireFileForModWriteEx(Segment->FileObject,
5082 &EndOffset,
5083 &ResourceToRelease);
5084 if (NT_SUCCESS(Status))
5085 {
5086 IoSetTopLevelIrp((PIRP)FSRTL_MOD_WRITE_TOP_LEVEL_IRP);
5087 }
5088 else
5089 {
5090 /* Make sure we will not try to release anything */
5091 ResourceToRelease = NULL;
5092 }
5093 }
5094 else
5095 {
5096 /* We don't have to lock. Say this is success */
5097 Status = STATUS_SUCCESS;
5098 }
5099
5100 /* Go ahead and write the page, if previous locking succeeded */
5101 if (NT_SUCCESS(Status))
5102 {
5103 DPRINT("Writing page at offset %I64d for file %wZ, Pageout: %s\n",
5104 Offset->QuadPart, &Segment->FileObject->FileName, PageOut ? "TRUE" : "FALSE");
5105 Status = MiWritePage(Segment, Offset->QuadPart, Page);
5106 }
5107
5108 if (PageOut)
5109 {
5110 IoSetTopLevelIrp(NULL);
5111 if (ResourceToRelease != NULL)
5112 {
5113 FsRtlReleaseFileForModWrite(Segment->FileObject, ResourceToRelease);
5114 }
5115 KeLowerIrql(OldIrql);
5116 }
5117 }
5118 else
5119 {
5120 /* This must only be called by the page-out path */
5121 ASSERT(PageOut);
5122
5123 /* And this must be for a shared section in a DLL */
5124 ASSERT(FlagOn(Segment->Image.Characteristics, IMAGE_SCN_MEM_SHARED));
5125
5126 SWAPENTRY SwapEntry = MmGetSavedSwapEntryPage(Page);
5127 if (!SwapEntry)
5128 {
5129 SwapEntry = MmAllocSwapPage();
5130 }
5131
5132 if (SwapEntry)
5133 {
5134 Status = MmWriteToSwapPage(SwapEntry, Page);
5135 if (NT_SUCCESS(Status))
5136 {
5137 MmSetSavedSwapEntryPage(Page, SwapEntry);
5138 }
5139 else
5140 {
5141 MmFreeSwapPage(SwapEntry);
5142 }
5143 }
5144 else
5145 {
5146 DPRINT1("Failed to allocate a swap page!\n");
5147 Status = STATUS_INSUFFICIENT_RESOURCES;
5148 }
5149 }
5150
5151 MmLockSectionSegment(Segment);
5152
5153 /* Get the entry again */
5154 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
5155 ASSERT(PFN_FROM_SSE(Entry) == Page);
5156
5157 if (!NT_SUCCESS(Status))
5158 {
5159 /* Damn, this failed. Consider this page as still dirty */
5160 DPRINT1("MiWritePage FAILED: Status 0x%08x!\n", Status);
5161 DirtyAgain = TRUE;
5162 }
5163 else
5164 {
5165 /* Check if someone dirtified this page while we were not looking */
5166 DirtyAgain = IS_DIRTY_SSE(Entry);
5167 }
5168
5169 /* Drop the reference we got, deleting the write altogether. */
5170 Entry = MAKE_SSE(Page << PAGE_SHIFT, SHARE_COUNT_FROM_SSE(Entry) - 1);
5171 if (DirtyAgain)
5172 {
5173 Entry = DIRTY_SSE(Entry);
5174 }
5175 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
5176 }
5177
5178 /* Were this page hanging there just for the sake of being present ? */
5179 if (!IS_DIRTY_SSE(Entry) && (SHARE_COUNT_FROM_SSE(Entry) == 0) && PageOut)
5180 {
5181 ULONG_PTR NewEntry = 0;
5182 /* Restore the swap entry here */
5183 if (!FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT))
5184 {
5185 SWAPENTRY SwapEntry = MmGetSavedSwapEntryPage(Page);
5186 if (SwapEntry)
5187 NewEntry = MAKE_SWAP_SSE(SwapEntry);
5188 }
5189
5190 /* Yes. Release it */
5191 MmSetPageEntrySectionSegment(Segment, Offset, NewEntry);
5192 MmReleasePageMemoryConsumer(MC_USER, Page);
5193 /* Tell the caller we released the page */
5194 return TRUE;
5195 }
5196
5197 return FALSE;
5198 }
5199
5200 NTSTATUS
5201 NTAPI
MmMakePagesDirty(_In_ PEPROCESS Process,_In_ PVOID Address,_In_ ULONG Length)5202 MmMakePagesDirty(
5203 _In_ PEPROCESS Process,
5204 _In_ PVOID Address,
5205 _In_ ULONG Length)
5206 {
5207 PMEMORY_AREA MemoryArea;
5208 PMM_SECTION_SEGMENT Segment;
5209 LARGE_INTEGER SegmentOffset, RangeEnd;
5210 PMMSUPPORT AddressSpace = Process ? &Process->Vm : MmGetKernelAddressSpace();
5211
5212 MmLockAddressSpace(AddressSpace);
5213
5214 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
5215 if (MemoryArea == NULL)
5216 {
5217 DPRINT1("Unable to find memory area at address %p.\n", Address);
5218 MmUnlockAddressSpace(AddressSpace);
5219 return STATUS_NOT_MAPPED_VIEW;
5220 }
5221
5222 /* Only supported in old Mm for now */
5223 ASSERT(MemoryArea->Type == MEMORY_AREA_SECTION_VIEW);
5224 /* For file mappings */
5225 ASSERT(MemoryArea->VadNode.u.VadFlags.VadType != VadImageMap);
5226
5227 Segment = MemoryArea->SectionData.Segment;
5228 MmLockSectionSegment(Segment);
5229
5230 SegmentOffset.QuadPart = PAGE_ROUND_DOWN(Address) - MA_GetStartingAddress(MemoryArea)
5231 + MemoryArea->SectionData.ViewOffset;
5232 RangeEnd.QuadPart = PAGE_ROUND_UP((ULONG_PTR)Address + Length) - MA_GetStartingAddress(MemoryArea)
5233 + MemoryArea->SectionData.ViewOffset;
5234
5235 DPRINT("MmMakePagesResident: Segment %p, 0x%I64x -> 0x%I64x\n", Segment, SegmentOffset.QuadPart, RangeEnd.QuadPart);
5236
5237 while (SegmentOffset.QuadPart < RangeEnd.QuadPart)
5238 {
5239 ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &SegmentOffset);
5240
5241 /* Let any pending read proceed */
5242 while (MM_IS_WAIT_PTE(Entry))
5243 {
5244 MmUnlockSectionSegment(Segment);
5245 MmUnlockAddressSpace(AddressSpace);
5246 KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
5247 MmLockAddressSpace(AddressSpace);
5248 MmLockSectionSegment(Segment);
5249 Entry = MmGetPageEntrySectionSegment(Segment, &SegmentOffset);
5250 }
5251
5252 /* We are called from Cc, this can't be backed by the page files */
5253 ASSERT(!IS_SWAP_FROM_SSE(Entry));
5254
5255 /* If there is no page there, there is nothing to make dirty */
5256 if (Entry != 0)
5257 {
5258 /* Dirtify the entry */
5259 MmSetPageEntrySectionSegment(Segment, &SegmentOffset, DIRTY_SSE(Entry));
5260 }
5261
5262 SegmentOffset.QuadPart += PAGE_SIZE;
5263 }
5264
5265 MmUnlockSectionSegment(Segment);
5266
5267 MmUnlockAddressSpace(AddressSpace);
5268 return STATUS_SUCCESS;
5269 }
5270
5271 NTSTATUS
5272 NTAPI
MmExtendSection(_In_ PVOID _Section,_Inout_ PLARGE_INTEGER NewSize)5273 MmExtendSection(
5274 _In_ PVOID _Section,
5275 _Inout_ PLARGE_INTEGER NewSize)
5276 {
5277 PSECTION Section = _Section;
5278
5279 /* It makes no sense to extend an image mapping */
5280 if (Section->u.Flags.Image)
5281 return STATUS_SECTION_NOT_EXTENDED;
5282
5283 /* Nor is it possible to extend a page file mapping */
5284 if (!Section->u.Flags.File)
5285 return STATUS_SECTION_NOT_EXTENDED;
5286
5287 if (!MiIsRosSectionObject(Section))
5288 return STATUS_NOT_IMPLEMENTED;
5289
5290 /* We just extend the sizes. Shrinking is a no-op ? */
5291 if (NewSize->QuadPart > Section->SizeOfSection.QuadPart)
5292 {
5293 PMM_SECTION_SEGMENT Segment = (PMM_SECTION_SEGMENT)Section->Segment;
5294 Section->SizeOfSection = *NewSize;
5295
5296 if (!Section->u.Flags.Reserve)
5297 {
5298 MmLockSectionSegment(Segment);
5299 if (Segment->RawLength.QuadPart < NewSize->QuadPart)
5300 {
5301 Segment->RawLength = *NewSize;
5302 Segment->Length.QuadPart = (NewSize->QuadPart + PAGE_SIZE - 1) & ~((LONGLONG)PAGE_SIZE);
5303 }
5304 MmUnlockSectionSegment(Segment);
5305 }
5306 }
5307
5308 return STATUS_SUCCESS;
5309 }
5310
5311 /* EOF */
5312