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