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