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 Protect = PAGE_READONLY; 1513 if (IS_SWAP_FROM_SSE(Entry) || PFN_FROM_SSE(Entry) != Page) 1514 { 1515 Protect = NewProtect; 1516 } 1517 } 1518 1519 if (MmIsPagePresent(Process, Address) || MmIsDisabledPage(Process, Address)) 1520 { 1521 MmSetPageProtect(Process, Address, 1522 Protect); 1523 } 1524 } 1525 } 1526 1527 MmUnlockSectionSegment(Segment); 1528 } 1529 1530 NTSTATUS 1531 NTAPI 1532 MmNotPresentFaultSectionView(PMMSUPPORT AddressSpace, 1533 MEMORY_AREA* MemoryArea, 1534 PVOID Address, 1535 BOOLEAN Locked) 1536 { 1537 LARGE_INTEGER Offset; 1538 PFN_NUMBER Page; 1539 NTSTATUS Status; 1540 PMM_SECTION_SEGMENT Segment; 1541 ULONG_PTR Entry; 1542 ULONG_PTR Entry1; 1543 ULONG Attributes; 1544 PMM_REGION Region; 1545 BOOLEAN HasSwapEntry; 1546 PVOID PAddress; 1547 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace); 1548 SWAPENTRY SwapEntry; 1549 1550 ASSERT(Locked); 1551 1552 /* 1553 * There is a window between taking the page fault and locking the 1554 * address space when another thread could load the page so we check 1555 * that. 1556 */ 1557 if (MmIsPagePresent(Process, Address)) 1558 { 1559 return STATUS_SUCCESS; 1560 } 1561 1562 if (MmIsDisabledPage(Process, Address)) 1563 { 1564 return STATUS_ACCESS_VIOLATION; 1565 } 1566 1567 /* 1568 * Check for the virtual memory area being deleted. 1569 */ 1570 if (MemoryArea->DeleteInProgress) 1571 { 1572 return STATUS_UNSUCCESSFUL; 1573 } 1574 1575 PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE); 1576 Offset.QuadPart = (ULONG_PTR)PAddress - MA_GetStartingAddress(MemoryArea) 1577 + MemoryArea->SectionData.ViewOffset; 1578 1579 Segment = MemoryArea->SectionData.Segment; 1580 Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea), 1581 &MemoryArea->SectionData.RegionListHead, 1582 Address, NULL); 1583 ASSERT(Region != NULL); 1584 1585 /* Check for a NOACCESS mapping */ 1586 if (Region->Protect & PAGE_NOACCESS) 1587 { 1588 return STATUS_ACCESS_VIOLATION; 1589 } 1590 1591 if (Region->Protect & PAGE_GUARD) 1592 { 1593 /* Remove it */ 1594 Status = MmAlterRegion(AddressSpace, (PVOID)MA_GetStartingAddress(MemoryArea), 1595 &MemoryArea->SectionData.RegionListHead, 1596 Address, PAGE_SIZE, Region->Type, Region->Protect & ~PAGE_GUARD, 1597 MmAlterViewAttributes); 1598 1599 if (!NT_SUCCESS(Status)) 1600 { 1601 DPRINT1("Removing PAGE_GUARD protection failed : 0x%08x.\n", Status); 1602 } 1603 1604 return STATUS_GUARD_PAGE_VIOLATION; 1605 } 1606 1607 HasSwapEntry = MmIsPageSwapEntry(Process, Address); 1608 1609 /* See if we should use a private page */ 1610 if (HasSwapEntry) 1611 { 1612 SWAPENTRY DummyEntry; 1613 1614 MmGetPageFileMapping(Process, Address, &SwapEntry); 1615 if (SwapEntry == MM_WAIT_ENTRY) 1616 { 1617 MmUnlockAddressSpace(AddressSpace); 1618 KeDelayExecutionThread(KernelMode, FALSE, &TinyTime); 1619 MmLockAddressSpace(AddressSpace); 1620 return STATUS_MM_RESTART_OPERATION; 1621 } 1622 1623 MI_SET_USAGE(MI_USAGE_SECTION); 1624 if (Process) MI_SET_PROCESS2(Process->ImageFileName); 1625 if (!Process) MI_SET_PROCESS2("Kernel Section"); 1626 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page); 1627 if (!NT_SUCCESS(Status)) 1628 { 1629 return STATUS_NO_MEMORY; 1630 } 1631 1632 /* 1633 * Must be private page we have swapped out. 1634 */ 1635 1636 /* 1637 * Sanity check 1638 */ 1639 MmDeletePageFileMapping(Process, Address, &DummyEntry); 1640 ASSERT(DummyEntry == SwapEntry); 1641 1642 /* Tell everyone else we are serving the fault. */ 1643 MmCreatePageFileMapping(Process, Address, MM_WAIT_ENTRY); 1644 1645 MmUnlockAddressSpace(AddressSpace); 1646 1647 Status = MmReadFromSwapPage(SwapEntry, Page); 1648 if (!NT_SUCCESS(Status)) 1649 { 1650 DPRINT1("MmReadFromSwapPage failed, status = %x\n", Status); 1651 KeBugCheck(MEMORY_MANAGEMENT); 1652 } 1653 1654 MmLockAddressSpace(AddressSpace); 1655 MmDeletePageFileMapping(Process, PAddress, &DummyEntry); 1656 ASSERT(DummyEntry == MM_WAIT_ENTRY); 1657 1658 Status = MmCreateVirtualMapping(Process, 1659 PAddress, 1660 Region->Protect, 1661 Page); 1662 if (!NT_SUCCESS(Status)) 1663 { 1664 DPRINT("MmCreateVirtualMapping failed, not out of memory\n"); 1665 KeBugCheck(MEMORY_MANAGEMENT); 1666 return Status; 1667 } 1668 1669 /* 1670 * Store the swap entry for later use. 1671 */ 1672 MmSetSavedSwapEntryPage(Page, SwapEntry); 1673 1674 /* 1675 * Add the page to the process's working set 1676 */ 1677 if (Process) MmInsertRmap(Page, Process, Address); 1678 /* 1679 * Finish the operation 1680 */ 1681 DPRINT("Address 0x%p\n", Address); 1682 return STATUS_SUCCESS; 1683 } 1684 1685 /* 1686 * Lock the segment 1687 */ 1688 MmLockSectionSegment(Segment); 1689 1690 /* 1691 * Satisfying a page fault on a map of /Device/PhysicalMemory is easy 1692 */ 1693 if ((*Segment->Flags) & MM_PHYSICALMEMORY_SEGMENT) 1694 { 1695 MmUnlockSectionSegment(Segment); 1696 /* 1697 * Just map the desired physical page 1698 */ 1699 Page = (PFN_NUMBER)(Offset.QuadPart >> PAGE_SHIFT); 1700 Status = MmCreatePhysicalMapping(Process, 1701 PAddress, 1702 Region->Protect, 1703 Page); 1704 if (!NT_SUCCESS(Status)) 1705 { 1706 DPRINT("MmCreateVirtualMappingUnsafe failed, not out of memory\n"); 1707 KeBugCheck(MEMORY_MANAGEMENT); 1708 return Status; 1709 } 1710 1711 /* 1712 * Cleanup and release locks 1713 */ 1714 DPRINT("Address 0x%p\n", Address); 1715 return STATUS_SUCCESS; 1716 } 1717 1718 /* 1719 * Check if this page needs to be mapped COW 1720 */ 1721 if ((Segment->WriteCopy) && 1722 (Region->Protect == PAGE_READWRITE || Region->Protect == PAGE_EXECUTE_READWRITE)) 1723 { 1724 Attributes = Region->Protect == PAGE_READWRITE ? PAGE_READONLY : PAGE_EXECUTE_READ; 1725 } 1726 else 1727 { 1728 Attributes = Region->Protect; 1729 } 1730 1731 1732 /* 1733 * Get the entry corresponding to the offset within the section 1734 */ 1735 Entry = MmGetPageEntrySectionSegment(Segment, &Offset); 1736 if (Entry == 0) 1737 { 1738 /* 1739 * If the entry is zero, then we need to load the page. 1740 */ 1741 if ((Offset.QuadPart >= (LONGLONG)PAGE_ROUND_UP(Segment->RawLength.QuadPart)) && (MemoryArea->VadNode.u.VadFlags.VadType == VadImageMap)) 1742 { 1743 /* We are beyond the data which is on file. Just get a new page. */ 1744 MI_SET_USAGE(MI_USAGE_SECTION); 1745 if (Process) MI_SET_PROCESS2(Process->ImageFileName); 1746 if (!Process) MI_SET_PROCESS2("Kernel Section"); 1747 Status = MmRequestPageMemoryConsumer(MC_USER, FALSE, &Page); 1748 if (!NT_SUCCESS(Status)) 1749 { 1750 MmUnlockSectionSegment(Segment); 1751 return STATUS_NO_MEMORY; 1752 } 1753 MmSetPageEntrySectionSegment(Segment, &Offset, MAKE_SSE(Page << PAGE_SHIFT, 1)); 1754 MmUnlockSectionSegment(Segment); 1755 1756 Status = MmCreateVirtualMapping(Process, PAddress, Attributes, Page); 1757 if (!NT_SUCCESS(Status)) 1758 { 1759 DPRINT1("Unable to create virtual mapping\n"); 1760 KeBugCheck(MEMORY_MANAGEMENT); 1761 } 1762 ASSERT(MmIsPagePresent(Process, PAddress)); 1763 if (Process) 1764 MmInsertRmap(Page, Process, Address); 1765 1766 DPRINT("Address 0x%p\n", Address); 1767 return STATUS_SUCCESS; 1768 } 1769 1770 MmUnlockSectionSegment(Segment); 1771 MmUnlockAddressSpace(AddressSpace); 1772 1773 /* The data must be paged in. Lock the file, so that the VDL doesn't get updated behind us. */ 1774 FsRtlAcquireFileExclusive(Segment->FileObject); 1775 1776 PFSRTL_COMMON_FCB_HEADER FcbHeader = Segment->FileObject->FsContext; 1777 1778 Status = MmMakeSegmentResident(Segment, Offset.QuadPart, PAGE_SIZE, &FcbHeader->ValidDataLength, FALSE); 1779 1780 FsRtlReleaseFile(Segment->FileObject); 1781 1782 /* Lock address space again */ 1783 MmLockAddressSpace(AddressSpace); 1784 if (!NT_SUCCESS(Status)) 1785 { 1786 if (Status == STATUS_NO_MEMORY) 1787 { 1788 return Status; 1789 } 1790 /* Damn */ 1791 DPRINT1("Failed to page data in!\n"); 1792 return STATUS_IN_PAGE_ERROR; 1793 } 1794 1795 /* Everything went fine. Restart the operation */ 1796 return STATUS_MM_RESTART_OPERATION; 1797 } 1798 else if (IS_SWAP_FROM_SSE(Entry)) 1799 { 1800 SWAPENTRY SwapEntry; 1801 1802 SwapEntry = SWAPENTRY_FROM_SSE(Entry); 1803 1804 /* See if a page op is running on this segment. */ 1805 if (SwapEntry == MM_WAIT_ENTRY) 1806 { 1807 MmUnlockSectionSegment(Segment); 1808 MmUnlockAddressSpace(AddressSpace); 1809 KeDelayExecutionThread(KernelMode, FALSE, &TinyTime); 1810 MmLockAddressSpace(AddressSpace); 1811 return STATUS_MM_RESTART_OPERATION; 1812 } 1813 1814 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page); 1815 if (!NT_SUCCESS(Status)) 1816 { 1817 MmUnlockSectionSegment(Segment); 1818 return STATUS_NO_MEMORY; 1819 } 1820 1821 /* 1822 * Release all our locks and read in the page from disk 1823 */ 1824 MmSetPageEntrySectionSegment(Segment, &Offset, MAKE_SWAP_SSE(MM_WAIT_ENTRY)); 1825 MmUnlockSectionSegment(Segment); 1826 1827 MmUnlockAddressSpace(AddressSpace); 1828 1829 Status = MmReadFromSwapPage(SwapEntry, Page); 1830 if (!NT_SUCCESS(Status)) 1831 { 1832 KeBugCheck(MEMORY_MANAGEMENT); 1833 } 1834 1835 /* 1836 * Relock the address space and segment 1837 */ 1838 MmLockAddressSpace(AddressSpace); 1839 MmLockSectionSegment(Segment); 1840 1841 /* 1842 * Check the entry. No one should change the status of a page 1843 * that has a pending page-in. 1844 */ 1845 Entry1 = MmGetPageEntrySectionSegment(Segment, &Offset); 1846 if (Entry1 != MAKE_SWAP_SSE(MM_WAIT_ENTRY)) 1847 { 1848 DPRINT1("Someone changed ppte entry while we slept (%x vs %x)\n", Entry, Entry1); 1849 KeBugCheck(MEMORY_MANAGEMENT); 1850 } 1851 1852 /* 1853 * Save the swap entry. 1854 */ 1855 MmSetSavedSwapEntryPage(Page, SwapEntry); 1856 1857 /* Map the page into the process address space */ 1858 Status = MmCreateVirtualMapping(Process, 1859 PAddress, 1860 Attributes, 1861 Page); 1862 if (!NT_SUCCESS(Status)) 1863 { 1864 DPRINT1("Unable to create virtual mapping\n"); 1865 KeBugCheck(MEMORY_MANAGEMENT); 1866 } 1867 if (Process) 1868 MmInsertRmap(Page, Process, Address); 1869 1870 /* 1871 * Mark the offset within the section as having valid, in-memory 1872 * data 1873 */ 1874 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1); 1875 MmSetPageEntrySectionSegment(Segment, &Offset, Entry); 1876 MmUnlockSectionSegment(Segment); 1877 1878 DPRINT("Address 0x%p\n", Address); 1879 return STATUS_SUCCESS; 1880 } 1881 else 1882 { 1883 /* We already have a page on this section offset. Map it into the process address space. */ 1884 Page = PFN_FROM_SSE(Entry); 1885 1886 Status = MmCreateVirtualMapping(Process, 1887 PAddress, 1888 Attributes, 1889 Page); 1890 if (!NT_SUCCESS(Status)) 1891 { 1892 DPRINT1("Unable to create virtual mapping\n"); 1893 KeBugCheck(MEMORY_MANAGEMENT); 1894 } 1895 1896 if (Process) 1897 MmInsertRmap(Page, Process, Address); 1898 1899 /* Take a reference on it */ 1900 MmSharePageEntrySectionSegment(Segment, &Offset); 1901 MmUnlockSectionSegment(Segment); 1902 1903 DPRINT("Address 0x%p\n", Address); 1904 return STATUS_SUCCESS; 1905 } 1906 } 1907 1908 NTSTATUS 1909 NTAPI 1910 MmAccessFaultSectionView(PMMSUPPORT AddressSpace, 1911 MEMORY_AREA* MemoryArea, 1912 PVOID Address, 1913 BOOLEAN Locked) 1914 { 1915 PMM_SECTION_SEGMENT Segment; 1916 PFN_NUMBER OldPage; 1917 PFN_NUMBER NewPage; 1918 PFN_NUMBER UnmappedPage; 1919 PVOID PAddress; 1920 LARGE_INTEGER Offset; 1921 PMM_REGION Region; 1922 ULONG_PTR Entry; 1923 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace); 1924 BOOLEAN Cow = FALSE; 1925 ULONG NewProtect; 1926 BOOLEAN Unmapped; 1927 NTSTATUS Status; 1928 1929 DPRINT("MmAccessFaultSectionView(%p, %p, %p)\n", AddressSpace, MemoryArea, Address); 1930 1931 /* Get the region for this address */ 1932 Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea), 1933 &MemoryArea->SectionData.RegionListHead, 1934 Address, NULL); 1935 ASSERT(Region != NULL); 1936 if (!(Region->Protect & PAGE_IS_WRITABLE)) 1937 return STATUS_ACCESS_VIOLATION; 1938 1939 /* Make sure we have a page mapping for this address. */ 1940 if (!MmIsPagePresent(Process, Address)) 1941 { 1942 NTSTATUS Status = MmNotPresentFaultSectionView(AddressSpace, MemoryArea, Address, Locked); 1943 if (!NT_SUCCESS(Status)) 1944 { 1945 /* This is invalid access ! */ 1946 return Status; 1947 } 1948 } 1949 1950 /* 1951 * Check if the page has already been set readwrite 1952 */ 1953 if (MmGetPageProtect(Process, Address) & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)) 1954 { 1955 DPRINT("Address 0x%p\n", Address); 1956 return STATUS_SUCCESS; 1957 } 1958 1959 /* Check if we are doing Copy-On-Write */ 1960 Segment = MemoryArea->SectionData.Segment; 1961 Cow = Segment->WriteCopy || (Region->Protect & PAGE_IS_WRITECOPY); 1962 1963 if (!Cow) 1964 { 1965 /* Simply update page protection and we're done */ 1966 MmSetPageProtect(Process, Address, Region->Protect); 1967 return STATUS_SUCCESS; 1968 } 1969 1970 /* Calculate the new protection & check if we should update the region */ 1971 NewProtect = Region->Protect; 1972 if (NewProtect & PAGE_IS_WRITECOPY) 1973 { 1974 NewProtect &= ~PAGE_IS_WRITECOPY; 1975 if (Region->Protect & PAGE_IS_EXECUTABLE) 1976 NewProtect |= PAGE_EXECUTE_READWRITE; 1977 else 1978 NewProtect |= PAGE_READWRITE; 1979 MmAlterRegion(AddressSpace, (PVOID)MA_GetStartingAddress(MemoryArea), 1980 &MemoryArea->SectionData.RegionListHead, 1981 Address, PAGE_SIZE, Region->Type, NewProtect, 1982 MmAlterViewAttributes); 1983 } 1984 1985 /* 1986 * Find the offset of the page 1987 */ 1988 PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE); 1989 Offset.QuadPart = (ULONG_PTR)PAddress - MA_GetStartingAddress(MemoryArea) 1990 + MemoryArea->SectionData.ViewOffset; 1991 1992 /* Get the page mapping this section offset. */ 1993 MmLockSectionSegment(Segment); 1994 Entry = MmGetPageEntrySectionSegment(Segment, &Offset); 1995 1996 /* Get the current page mapping for the process */ 1997 ASSERT(MmIsPagePresent(Process, PAddress)); 1998 OldPage = MmGetPfnForProcess(Process, PAddress); 1999 ASSERT(OldPage != 0); 2000 2001 if (IS_SWAP_FROM_SSE(Entry) || 2002 PFN_FROM_SSE(Entry) != OldPage) 2003 { 2004 MmUnlockSectionSegment(Segment); 2005 /* This is a private page. We must only change the page protection. */ 2006 MmSetPageProtect(Process, PAddress, NewProtect); 2007 return STATUS_SUCCESS; 2008 } 2009 2010 /* 2011 * Allocate a page 2012 */ 2013 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &NewPage); 2014 if (!NT_SUCCESS(Status)) 2015 { 2016 MmUnlockSectionSegment(Segment); 2017 return STATUS_NO_MEMORY; 2018 } 2019 2020 /* 2021 * Copy the old page 2022 */ 2023 NT_VERIFY(NT_SUCCESS(MiCopyFromUserPage(NewPage, PAddress))); 2024 2025 /* 2026 * Unshare the old page. 2027 */ 2028 DPRINT("Swapping page (Old %x New %x)\n", OldPage, NewPage); 2029 Unmapped = MmDeleteVirtualMapping(Process, PAddress, NULL, &UnmappedPage); 2030 if (!Unmapped || (UnmappedPage != OldPage)) 2031 { 2032 /* Uh , we had a page just before, but suddenly it changes. Someone corrupted us. */ 2033 KeBugCheckEx(MEMORY_MANAGEMENT, 2034 (ULONG_PTR)Process, 2035 (ULONG_PTR)PAddress, 2036 (ULONG_PTR)__FILE__, 2037 __LINE__); 2038 } 2039 2040 if (Process) 2041 MmDeleteRmap(OldPage, Process, PAddress); 2042 MmUnsharePageEntrySectionSegment(MemoryArea, Segment, &Offset, FALSE, FALSE, NULL); 2043 MmUnlockSectionSegment(Segment); 2044 2045 /* 2046 * Set the PTE to point to the new page 2047 */ 2048 if (!NT_SUCCESS(MmCreateVirtualMapping(Process, PAddress, NewProtect, NewPage))) 2049 { 2050 DPRINT1("MmCreateVirtualMapping failed, unable to create virtual mapping, not out of memory\n"); 2051 KeBugCheck(MEMORY_MANAGEMENT); 2052 } 2053 2054 if (Process) 2055 MmInsertRmap(NewPage, Process, PAddress); 2056 2057 DPRINT("Address 0x%p\n", Address); 2058 return STATUS_SUCCESS; 2059 } 2060 2061 NTSTATUS 2062 NTAPI 2063 MmProtectSectionView(PMMSUPPORT AddressSpace, 2064 PMEMORY_AREA MemoryArea, 2065 PVOID BaseAddress, 2066 SIZE_T Length, 2067 ULONG Protect, 2068 PULONG OldProtect) 2069 { 2070 PMM_REGION Region; 2071 NTSTATUS Status; 2072 ULONG_PTR MaxLength; 2073 2074 MaxLength = MA_GetEndingAddress(MemoryArea) - (ULONG_PTR)BaseAddress; 2075 if (Length > MaxLength) 2076 Length = (ULONG)MaxLength; 2077 2078 Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea), 2079 &MemoryArea->SectionData.RegionListHead, 2080 BaseAddress, NULL); 2081 ASSERT(Region != NULL); 2082 2083 if ((MemoryArea->Flags & SEC_NO_CHANGE) && 2084 Region->Protect != Protect) 2085 { 2086 return STATUS_INVALID_PAGE_PROTECTION; 2087 } 2088 2089 *OldProtect = Region->Protect; 2090 Status = MmAlterRegion(AddressSpace, (PVOID)MA_GetStartingAddress(MemoryArea), 2091 &MemoryArea->SectionData.RegionListHead, 2092 BaseAddress, Length, Region->Type, Protect, 2093 MmAlterViewAttributes); 2094 2095 return Status; 2096 } 2097 2098 NTSTATUS NTAPI 2099 MmQuerySectionView(PMEMORY_AREA MemoryArea, 2100 PVOID Address, 2101 PMEMORY_BASIC_INFORMATION Info, 2102 PSIZE_T ResultLength) 2103 { 2104 PMM_REGION Region; 2105 PVOID RegionBaseAddress; 2106 PMM_SECTION_SEGMENT Segment; 2107 2108 Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea), 2109 &MemoryArea->SectionData.RegionListHead, 2110 Address, &RegionBaseAddress); 2111 if (Region == NULL) 2112 { 2113 return STATUS_UNSUCCESSFUL; 2114 } 2115 2116 if (MemoryArea->VadNode.u.VadFlags.VadType == VadImageMap) 2117 { 2118 Segment = MemoryArea->SectionData.Segment; 2119 Info->AllocationBase = (PUCHAR)MA_GetStartingAddress(MemoryArea) - Segment->Image.VirtualAddress; 2120 Info->Type = MEM_IMAGE; 2121 } 2122 else 2123 { 2124 Info->AllocationBase = (PVOID)MA_GetStartingAddress(MemoryArea); 2125 Info->Type = MEM_MAPPED; 2126 } 2127 Info->BaseAddress = RegionBaseAddress; 2128 Info->AllocationProtect = MmProtectToValue[MemoryArea->VadNode.u.VadFlags.Protection]; 2129 Info->RegionSize = Region->Length; 2130 Info->State = MEM_COMMIT; 2131 Info->Protect = Region->Protect; 2132 2133 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION); 2134 return STATUS_SUCCESS; 2135 } 2136 2137 VOID NTAPI 2138 MmpDeleteSection(PVOID ObjectBody) 2139 { 2140 PSECTION Section = ObjectBody; 2141 2142 /* Check if it's an ARM3, or ReactOS section */ 2143 if (!MiIsRosSectionObject(Section)) 2144 { 2145 MiDeleteARM3Section(ObjectBody); 2146 return; 2147 } 2148 2149 DPRINT("MmpDeleteSection(ObjectBody %p)\n", ObjectBody); 2150 if (Section->u.Flags.Image) 2151 { 2152 PMM_IMAGE_SECTION_OBJECT ImageSectionObject = (PMM_IMAGE_SECTION_OBJECT)Section->Segment; 2153 2154 /* 2155 * NOTE: Section->ImageSection can be NULL for short time 2156 * during the section creating. If we fail for some reason 2157 * until the image section is properly initialized we shouldn't 2158 * process further here. 2159 */ 2160 if (Section->Segment == NULL) 2161 return; 2162 2163 KIRQL OldIrql = MiAcquirePfnLock(); 2164 ImageSectionObject->SectionCount--; 2165 2166 /* We just dereference the first segment */ 2167 ASSERT(ImageSectionObject->RefCount > 0); 2168 /* MmDereferenceSegmentWithLock releases PFN lock */ 2169 MmDereferenceSegmentWithLock(ImageSectionObject->Segments, OldIrql); 2170 } 2171 else 2172 { 2173 PMM_SECTION_SEGMENT Segment = (PMM_SECTION_SEGMENT)Section->Segment; 2174 2175 /* 2176 * NOTE: Section->Segment can be NULL for short time 2177 * during the section creating. 2178 */ 2179 if (Segment == NULL) 2180 return; 2181 2182 KIRQL OldIrql = MiAcquirePfnLock(); 2183 Segment->SectionCount--; 2184 2185 /* MmDereferenceSegmentWithLock releases PFN lock */ 2186 MmDereferenceSegmentWithLock(Segment, OldIrql); 2187 } 2188 } 2189 2190 VOID NTAPI 2191 MmpCloseSection(IN PEPROCESS Process OPTIONAL, 2192 IN PVOID Object, 2193 IN ACCESS_MASK GrantedAccess, 2194 IN ULONG ProcessHandleCount, 2195 IN ULONG SystemHandleCount) 2196 { 2197 DPRINT("MmpCloseSection(OB %p, HC %lu)\n", Object, ProcessHandleCount); 2198 } 2199 2200 CODE_SEG("INIT") 2201 NTSTATUS 2202 NTAPI 2203 MmCreatePhysicalMemorySection(VOID) 2204 { 2205 PSECTION PhysSection; 2206 NTSTATUS Status; 2207 OBJECT_ATTRIBUTES Obj; 2208 UNICODE_STRING Name = RTL_CONSTANT_STRING(L"\\Device\\PhysicalMemory"); 2209 LARGE_INTEGER SectionSize; 2210 HANDLE Handle; 2211 PMM_SECTION_SEGMENT Segment; 2212 2213 /* 2214 * Create the section mapping physical memory 2215 */ 2216 SectionSize.QuadPart = MmHighestPhysicalPage * PAGE_SIZE; 2217 InitializeObjectAttributes(&Obj, 2218 &Name, 2219 OBJ_PERMANENT | OBJ_KERNEL_EXCLUSIVE, 2220 NULL, 2221 NULL); 2222 /* 2223 * Create the Object 2224 */ 2225 Status = ObCreateObject(KernelMode, 2226 MmSectionObjectType, 2227 &Obj, 2228 ExGetPreviousMode(), 2229 NULL, 2230 sizeof(*PhysSection), 2231 0, 2232 0, 2233 (PVOID*)&PhysSection); 2234 if (!NT_SUCCESS(Status)) 2235 { 2236 DPRINT1("MmCreatePhysicalMemorySection: failed to create object (0x%lx)\n", Status); 2237 return Status; 2238 } 2239 2240 /* 2241 * Initialize it 2242 */ 2243 RtlZeroMemory(PhysSection, sizeof(*PhysSection)); 2244 2245 /* Mark this as a "ROS Section" */ 2246 PhysSection->u.Flags.filler = 1; 2247 PhysSection->InitialPageProtection = PAGE_EXECUTE_READWRITE; 2248 PhysSection->u.Flags.PhysicalMemory = 1; 2249 PhysSection->SizeOfSection = SectionSize; 2250 Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT), 2251 TAG_MM_SECTION_SEGMENT); 2252 if (Segment == NULL) 2253 { 2254 ObDereferenceObject(PhysSection); 2255 return STATUS_NO_MEMORY; 2256 } 2257 RtlZeroMemory(Segment, sizeof(MM_SECTION_SEGMENT)); 2258 PhysSection->Segment = (PSEGMENT)Segment; 2259 Segment->RefCount = 1; 2260 2261 Segment->ReferenceCount = &Segment->RefCount; 2262 Segment->Flags = &Segment->SegFlags; 2263 2264 ExInitializeFastMutex(&Segment->Lock); 2265 Segment->Image.FileOffset = 0; 2266 Segment->Protection = PAGE_EXECUTE_READWRITE; 2267 Segment->RawLength = SectionSize; 2268 Segment->Length = SectionSize; 2269 Segment->SegFlags = MM_PHYSICALMEMORY_SEGMENT; 2270 Segment->WriteCopy = FALSE; 2271 Segment->Image.VirtualAddress = 0; 2272 Segment->Image.Characteristics = 0; 2273 MiInitializeSectionPageTable(Segment); 2274 2275 Status = ObInsertObject(PhysSection, 2276 NULL, 2277 SECTION_ALL_ACCESS, 2278 0, 2279 NULL, 2280 &Handle); 2281 if (!NT_SUCCESS(Status)) 2282 { 2283 ObDereferenceObject(PhysSection); 2284 return Status; 2285 } 2286 ObCloseHandle(Handle, KernelMode); 2287 2288 return STATUS_SUCCESS; 2289 } 2290 2291 CODE_SEG("INIT") 2292 NTSTATUS 2293 NTAPI 2294 MmInitSectionImplementation(VOID) 2295 { 2296 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer; 2297 UNICODE_STRING Name; 2298 2299 DPRINT("Creating Section Object Type\n"); 2300 2301 /* Initialize the section based root */ 2302 ASSERT(MmSectionBasedRoot.NumberGenericTableElements == 0); 2303 MmSectionBasedRoot.BalancedRoot.u1.Parent = &MmSectionBasedRoot.BalancedRoot; 2304 2305 /* Initialize the Section object type */ 2306 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer)); 2307 RtlInitUnicodeString(&Name, L"Section"); 2308 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer); 2309 ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(SECTION); 2310 ObjectTypeInitializer.PoolType = PagedPool; 2311 ObjectTypeInitializer.UseDefaultObject = TRUE; 2312 ObjectTypeInitializer.GenericMapping = MmpSectionMapping; 2313 ObjectTypeInitializer.DeleteProcedure = MmpDeleteSection; 2314 ObjectTypeInitializer.CloseProcedure = MmpCloseSection; 2315 ObjectTypeInitializer.ValidAccessMask = SECTION_ALL_ACCESS; 2316 ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK; 2317 ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &MmSectionObjectType); 2318 2319 MmCreatePhysicalMemorySection(); 2320 2321 return STATUS_SUCCESS; 2322 } 2323 2324 static 2325 NTSTATUS 2326 NTAPI 2327 MmCreateDataFileSection(PSECTION *SectionObject, 2328 ACCESS_MASK DesiredAccess, 2329 POBJECT_ATTRIBUTES ObjectAttributes, 2330 PLARGE_INTEGER UMaximumSize, 2331 ULONG SectionPageProtection, 2332 ULONG AllocationAttributes, 2333 PFILE_OBJECT FileObject, 2334 BOOLEAN GotFileHandle) 2335 /* 2336 * Create a section backed by a data file 2337 */ 2338 { 2339 PSECTION Section; 2340 NTSTATUS Status; 2341 LARGE_INTEGER MaximumSize; 2342 PMM_SECTION_SEGMENT Segment; 2343 KIRQL OldIrql; 2344 2345 /* 2346 * Create the section 2347 */ 2348 Status = ObCreateObject(ExGetPreviousMode(), 2349 MmSectionObjectType, 2350 ObjectAttributes, 2351 ExGetPreviousMode(), 2352 NULL, 2353 sizeof(*Section), 2354 0, 2355 0, 2356 (PVOID*)&Section); 2357 if (!NT_SUCCESS(Status)) 2358 { 2359 return Status; 2360 } 2361 /* 2362 * Initialize it 2363 */ 2364 RtlZeroMemory(Section, sizeof(*Section)); 2365 2366 /* Mark this as a "ROS" section */ 2367 Section->u.Flags.filler = 1; 2368 Section->InitialPageProtection = SectionPageProtection; 2369 Section->u.Flags.File = 1; 2370 2371 if (AllocationAttributes & SEC_NO_CHANGE) 2372 Section->u.Flags.NoChange = 1; 2373 if (AllocationAttributes & SEC_RESERVE) 2374 Section->u.Flags.Reserve = 1; 2375 2376 if (!GotFileHandle) 2377 { 2378 ASSERT(UMaximumSize != NULL); 2379 // ASSERT(UMaximumSize->QuadPart != 0); 2380 MaximumSize = *UMaximumSize; 2381 } 2382 else 2383 { 2384 LARGE_INTEGER FileSize; 2385 Status = FsRtlGetFileSize(FileObject, &FileSize); 2386 if (!NT_SUCCESS(Status)) 2387 { 2388 ObDereferenceObject(Section); 2389 return Status; 2390 } 2391 2392 /* 2393 * FIXME: Revise this once a locking order for file size changes is 2394 * decided 2395 */ 2396 if ((UMaximumSize != NULL) && (UMaximumSize->QuadPart != 0)) 2397 { 2398 MaximumSize = *UMaximumSize; 2399 } 2400 else 2401 { 2402 MaximumSize = FileSize; 2403 /* Mapping zero-sized files isn't allowed. */ 2404 if (MaximumSize.QuadPart == 0) 2405 { 2406 ObDereferenceObject(Section); 2407 return STATUS_MAPPED_FILE_SIZE_ZERO; 2408 } 2409 } 2410 2411 if (MaximumSize.QuadPart > FileSize.QuadPart) 2412 { 2413 Status = IoSetInformation(FileObject, 2414 FileEndOfFileInformation, 2415 sizeof(LARGE_INTEGER), 2416 &MaximumSize); 2417 if (!NT_SUCCESS(Status)) 2418 { 2419 ObDereferenceObject(Section); 2420 return STATUS_SECTION_NOT_EXTENDED; 2421 } 2422 } 2423 } 2424 2425 if (FileObject->SectionObjectPointer == NULL) 2426 { 2427 ObDereferenceObject(Section); 2428 return STATUS_INVALID_FILE_FOR_SECTION; 2429 } 2430 2431 /* 2432 * Lock the file 2433 */ 2434 Status = MmspWaitForFileLock(FileObject); 2435 if (Status != STATUS_SUCCESS) 2436 { 2437 ObDereferenceObject(Section); 2438 return Status; 2439 } 2440 2441 /* Lock the PFN lock while messing with Section Object pointers */ 2442 grab_segment: 2443 OldIrql = MiAcquirePfnLock(); 2444 Segment = FileObject->SectionObjectPointer->DataSectionObject; 2445 2446 while (Segment && (Segment->SegFlags & (MM_SEGMENT_INDELETE | MM_SEGMENT_INCREATE))) 2447 { 2448 MiReleasePfnLock(OldIrql); 2449 KeDelayExecutionThread(KernelMode, FALSE, &TinyTime); 2450 OldIrql = MiAcquirePfnLock(); 2451 Segment = FileObject->SectionObjectPointer->DataSectionObject; 2452 } 2453 2454 /* 2455 * If this file hasn't been mapped as a data file before then allocate a 2456 * section segment to describe the data file mapping 2457 */ 2458 if (Segment == NULL) 2459 { 2460 /* Release the lock. ExAllocatePoolWithTag might acquire it */ 2461 MiReleasePfnLock(OldIrql); 2462 2463 Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT), 2464 TAG_MM_SECTION_SEGMENT); 2465 if (Segment == NULL) 2466 { 2467 //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE); 2468 ObDereferenceObject(Section); 2469 return STATUS_NO_MEMORY; 2470 } 2471 2472 /* We are creating it */ 2473 RtlZeroMemory(Segment, sizeof(*Segment)); 2474 Segment->SegFlags = MM_DATAFILE_SEGMENT | MM_SEGMENT_INCREATE; 2475 Segment->RefCount = 1; 2476 2477 /* Acquire lock again */ 2478 OldIrql = MiAcquirePfnLock(); 2479 2480 if (FileObject->SectionObjectPointer->DataSectionObject != NULL) 2481 { 2482 /* Well that's bad luck. Restart it all over */ 2483 MiReleasePfnLock(OldIrql); 2484 ExFreePoolWithTag(Segment, TAG_MM_SECTION_SEGMENT); 2485 goto grab_segment; 2486 } 2487 2488 FileObject->SectionObjectPointer->DataSectionObject = Segment; 2489 2490 /* We're safe to release the lock now */ 2491 MiReleasePfnLock(OldIrql); 2492 2493 Section->Segment = (PSEGMENT)Segment; 2494 2495 /* Self-referencing segment */ 2496 Segment->Flags = &Segment->SegFlags; 2497 Segment->ReferenceCount = &Segment->RefCount; 2498 2499 Segment->SectionCount = 1; 2500 2501 ExInitializeFastMutex(&Segment->Lock); 2502 Segment->FileObject = FileObject; 2503 ObReferenceObject(FileObject); 2504 2505 Segment->Image.FileOffset = 0; 2506 Segment->Protection = SectionPageProtection; 2507 2508 Segment->Image.Characteristics = 0; 2509 Segment->WriteCopy = (SectionPageProtection & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY)); 2510 if (AllocationAttributes & SEC_RESERVE) 2511 { 2512 Segment->Length.QuadPart = Segment->RawLength.QuadPart = 0; 2513 } 2514 else 2515 { 2516 Segment->RawLength.QuadPart = MaximumSize.QuadPart; 2517 Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart); 2518 } 2519 Segment->Image.VirtualAddress = 0; 2520 MiInitializeSectionPageTable(Segment); 2521 2522 /* We're good to use it now */ 2523 OldIrql = MiAcquirePfnLock(); 2524 Segment->SegFlags &= ~MM_SEGMENT_INCREATE; 2525 MiReleasePfnLock(OldIrql); 2526 } 2527 else 2528 { 2529 Section->Segment = (PSEGMENT)Segment; 2530 InterlockedIncrement64(&Segment->RefCount); 2531 InterlockedIncrementUL(&Segment->SectionCount); 2532 2533 MiReleasePfnLock(OldIrql); 2534 2535 MmLockSectionSegment(Segment); 2536 2537 if (MaximumSize.QuadPart > Segment->RawLength.QuadPart && 2538 !(AllocationAttributes & SEC_RESERVE)) 2539 { 2540 Segment->RawLength.QuadPart = MaximumSize.QuadPart; 2541 Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart); 2542 } 2543 2544 MmUnlockSectionSegment(Segment); 2545 } 2546 Section->SizeOfSection = MaximumSize; 2547 2548 //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE); 2549 *SectionObject = Section; 2550 return STATUS_SUCCESS; 2551 } 2552 2553 /* 2554 TODO: not that great (declaring loaders statically, having to declare all of 2555 them, having to keep them extern, etc.), will fix in the future 2556 */ 2557 extern NTSTATUS NTAPI PeFmtCreateSection 2558 ( 2559 IN CONST VOID * FileHeader, 2560 IN SIZE_T FileHeaderSize, 2561 IN PVOID File, 2562 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject, 2563 OUT PULONG Flags, 2564 IN PEXEFMT_CB_READ_FILE ReadFileCb, 2565 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb 2566 ); 2567 2568 extern NTSTATUS NTAPI ElfFmtCreateSection 2569 ( 2570 IN CONST VOID * FileHeader, 2571 IN SIZE_T FileHeaderSize, 2572 IN PVOID File, 2573 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject, 2574 OUT PULONG Flags, 2575 IN PEXEFMT_CB_READ_FILE ReadFileCb, 2576 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb 2577 ); 2578 2579 static PEXEFMT_LOADER ExeFmtpLoaders[] = 2580 { 2581 PeFmtCreateSection, 2582 #ifdef __ELF 2583 ElfFmtCreateSection 2584 #endif 2585 }; 2586 2587 static 2588 PMM_SECTION_SEGMENT 2589 NTAPI 2590 ExeFmtpAllocateSegments(IN ULONG NrSegments) 2591 { 2592 SIZE_T SizeOfSegments; 2593 PMM_SECTION_SEGMENT Segments; 2594 2595 /* TODO: check for integer overflow */ 2596 SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * NrSegments; 2597 2598 Segments = ExAllocatePoolWithTag(NonPagedPool, 2599 SizeOfSegments, 2600 TAG_MM_SECTION_SEGMENT); 2601 2602 if(Segments) 2603 RtlZeroMemory(Segments, SizeOfSegments); 2604 2605 return Segments; 2606 } 2607 static 2608 NTSTATUS 2609 NTAPI 2610 ExeFmtpReadFile(IN PVOID File, 2611 IN PLARGE_INTEGER Offset, 2612 IN ULONG Length, 2613 OUT PVOID * Data, 2614 OUT PVOID * AllocBase, 2615 OUT PULONG ReadSize) 2616 { 2617 NTSTATUS Status; 2618 LARGE_INTEGER FileOffset; 2619 ULONG AdjustOffset; 2620 ULONG OffsetAdjustment; 2621 ULONG BufferSize; 2622 ULONG UsedSize; 2623 PVOID Buffer; 2624 PFILE_OBJECT FileObject = File; 2625 IO_STATUS_BLOCK Iosb; 2626 2627 ASSERT_IRQL_LESS(DISPATCH_LEVEL); 2628 2629 if(Length == 0) 2630 { 2631 KeBugCheck(MEMORY_MANAGEMENT); 2632 } 2633 2634 FileOffset = *Offset; 2635 2636 /* Negative/special offset: it cannot be used in this context */ 2637 if(FileOffset.u.HighPart < 0) 2638 { 2639 KeBugCheck(MEMORY_MANAGEMENT); 2640 } 2641 2642 AdjustOffset = PAGE_ROUND_DOWN(FileOffset.u.LowPart); 2643 OffsetAdjustment = FileOffset.u.LowPart - AdjustOffset; 2644 FileOffset.u.LowPart = AdjustOffset; 2645 2646 BufferSize = Length + OffsetAdjustment; 2647 BufferSize = PAGE_ROUND_UP(BufferSize); 2648 2649 /* 2650 * It's ok to use paged pool, because this is a temporary buffer only used in 2651 * the loading of executables. The assumption is that MmCreateSection is 2652 * always called at low IRQLs and that these buffers don't survive a brief 2653 * initialization phase 2654 */ 2655 Buffer = ExAllocatePoolWithTag(PagedPool, BufferSize, 'rXmM'); 2656 if (!Buffer) 2657 { 2658 return STATUS_INSUFFICIENT_RESOURCES; 2659 } 2660 2661 Status = MiSimpleRead(FileObject, &FileOffset, Buffer, BufferSize, TRUE, &Iosb); 2662 2663 UsedSize = (ULONG)Iosb.Information; 2664 2665 if(NT_SUCCESS(Status) && UsedSize < OffsetAdjustment) 2666 { 2667 Status = STATUS_IN_PAGE_ERROR; 2668 ASSERT(!NT_SUCCESS(Status)); 2669 } 2670 2671 if(NT_SUCCESS(Status)) 2672 { 2673 *Data = (PVOID)((ULONG_PTR)Buffer + OffsetAdjustment); 2674 *AllocBase = Buffer; 2675 *ReadSize = UsedSize - OffsetAdjustment; 2676 } 2677 else 2678 { 2679 ExFreePoolWithTag(Buffer, 'rXmM'); 2680 } 2681 2682 return Status; 2683 } 2684 2685 #ifdef NASSERT 2686 # define MmspAssertSegmentsSorted(OBJ_) ((void)0) 2687 # define MmspAssertSegmentsNoOverlap(OBJ_) ((void)0) 2688 # define MmspAssertSegmentsPageAligned(OBJ_) ((void)0) 2689 #else 2690 static 2691 VOID 2692 NTAPI 2693 MmspAssertSegmentsSorted(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject) 2694 { 2695 ULONG i; 2696 2697 for( i = 1; i < ImageSectionObject->NrSegments; ++ i ) 2698 { 2699 ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >= 2700 ImageSectionObject->Segments[i - 1].Image.VirtualAddress); 2701 } 2702 } 2703 2704 static 2705 VOID 2706 NTAPI 2707 MmspAssertSegmentsNoOverlap(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject) 2708 { 2709 ULONG i; 2710 2711 MmspAssertSegmentsSorted(ImageSectionObject); 2712 2713 for( i = 0; i < ImageSectionObject->NrSegments; ++ i ) 2714 { 2715 ASSERT(ImageSectionObject->Segments[i].Length.QuadPart > 0); 2716 2717 if(i > 0) 2718 { 2719 ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >= 2720 (ImageSectionObject->Segments[i - 1].Image.VirtualAddress + 2721 ImageSectionObject->Segments[i - 1].Length.QuadPart)); 2722 } 2723 } 2724 } 2725 2726 static 2727 VOID 2728 NTAPI 2729 MmspAssertSegmentsPageAligned(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject) 2730 { 2731 ULONG i; 2732 2733 for( i = 0; i < ImageSectionObject->NrSegments; ++ i ) 2734 { 2735 ASSERT((ImageSectionObject->Segments[i].Image.VirtualAddress % PAGE_SIZE) == 0); 2736 ASSERT((ImageSectionObject->Segments[i].Length.QuadPart % PAGE_SIZE) == 0); 2737 } 2738 } 2739 #endif 2740 2741 static 2742 int 2743 __cdecl 2744 MmspCompareSegments(const void * x, 2745 const void * y) 2746 { 2747 const MM_SECTION_SEGMENT *Segment1 = (const MM_SECTION_SEGMENT *)x; 2748 const MM_SECTION_SEGMENT *Segment2 = (const MM_SECTION_SEGMENT *)y; 2749 2750 if (Segment1->Image.VirtualAddress > Segment2->Image.VirtualAddress) 2751 return 1; 2752 else if (Segment1->Image.VirtualAddress < Segment2->Image.VirtualAddress) 2753 return -1; 2754 else 2755 return 0; 2756 } 2757 2758 /* 2759 * Ensures an image section's segments are sorted in memory 2760 */ 2761 static 2762 VOID 2763 NTAPI 2764 MmspSortSegments(IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject, 2765 IN ULONG Flags) 2766 { 2767 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_SORTED) 2768 { 2769 MmspAssertSegmentsSorted(ImageSectionObject); 2770 } 2771 else 2772 { 2773 qsort(ImageSectionObject->Segments, 2774 ImageSectionObject->NrSegments, 2775 sizeof(ImageSectionObject->Segments[0]), 2776 MmspCompareSegments); 2777 } 2778 } 2779 2780 2781 /* 2782 * Ensures an image section's segments don't overlap in memory and don't have 2783 * gaps and don't have a null size. We let them map to overlapping file regions, 2784 * though - that's not necessarily an error 2785 */ 2786 static 2787 BOOLEAN 2788 NTAPI 2789 MmspCheckSegmentBounds 2790 ( 2791 IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject, 2792 IN ULONG Flags 2793 ) 2794 { 2795 ULONG i; 2796 2797 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_NO_OVERLAP) 2798 { 2799 MmspAssertSegmentsNoOverlap(ImageSectionObject); 2800 return TRUE; 2801 } 2802 2803 ASSERT(ImageSectionObject->NrSegments >= 1); 2804 2805 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i ) 2806 { 2807 if(ImageSectionObject->Segments[i].Length.QuadPart == 0) 2808 { 2809 return FALSE; 2810 } 2811 2812 if(i > 0) 2813 { 2814 /* 2815 * TODO: relax the limitation on gaps. For example, gaps smaller than a 2816 * page could be OK (Windows seems to be OK with them), and larger gaps 2817 * could lead to image sections spanning several discontiguous regions 2818 * (NtMapViewOfSection could then refuse to map them, and they could 2819 * e.g. only be allowed as parameters to NtCreateProcess, like on UNIX) 2820 */ 2821 if ((ImageSectionObject->Segments[i - 1].Image.VirtualAddress + 2822 ImageSectionObject->Segments[i - 1].Length.QuadPart) != 2823 ImageSectionObject->Segments[i].Image.VirtualAddress) 2824 { 2825 return FALSE; 2826 } 2827 } 2828 } 2829 2830 return TRUE; 2831 } 2832 2833 /* 2834 * Merges and pads an image section's segments until they all are page-aligned 2835 * and have a size that is a multiple of the page size 2836 */ 2837 static 2838 BOOLEAN 2839 NTAPI 2840 MmspPageAlignSegments 2841 ( 2842 IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject, 2843 IN ULONG Flags 2844 ) 2845 { 2846 ULONG i; 2847 ULONG LastSegment; 2848 PMM_SECTION_SEGMENT EffectiveSegment; 2849 2850 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED) 2851 { 2852 MmspAssertSegmentsPageAligned(ImageSectionObject); 2853 return TRUE; 2854 } 2855 2856 LastSegment = 0; 2857 EffectiveSegment = &ImageSectionObject->Segments[LastSegment]; 2858 2859 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i ) 2860 { 2861 /* 2862 * The first segment requires special handling 2863 */ 2864 if (i == 0) 2865 { 2866 ULONG_PTR VirtualAddress; 2867 ULONG_PTR VirtualOffset; 2868 2869 VirtualAddress = EffectiveSegment->Image.VirtualAddress; 2870 2871 /* Round down the virtual address to the nearest page */ 2872 EffectiveSegment->Image.VirtualAddress = PAGE_ROUND_DOWN(VirtualAddress); 2873 2874 /* Round up the virtual size to the nearest page */ 2875 EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(VirtualAddress + EffectiveSegment->Length.QuadPart) - 2876 EffectiveSegment->Image.VirtualAddress; 2877 2878 /* Adjust the raw address and size */ 2879 VirtualOffset = VirtualAddress - EffectiveSegment->Image.VirtualAddress; 2880 2881 if (EffectiveSegment->Image.FileOffset < VirtualOffset) 2882 { 2883 return FALSE; 2884 } 2885 2886 /* 2887 * Garbage in, garbage out: unaligned base addresses make the file 2888 * offset point in curious and odd places, but that's what we were 2889 * asked for 2890 */ 2891 EffectiveSegment->Image.FileOffset -= VirtualOffset; 2892 EffectiveSegment->RawLength.QuadPart += VirtualOffset; 2893 } 2894 else 2895 { 2896 PMM_SECTION_SEGMENT Segment = &ImageSectionObject->Segments[i]; 2897 ULONG_PTR EndOfEffectiveSegment; 2898 2899 EndOfEffectiveSegment = (ULONG_PTR)(EffectiveSegment->Image.VirtualAddress + EffectiveSegment->Length.QuadPart); 2900 ASSERT((EndOfEffectiveSegment % PAGE_SIZE) == 0); 2901 2902 /* 2903 * The current segment begins exactly where the current effective 2904 * segment ended, therefore beginning a new effective segment 2905 */ 2906 if (EndOfEffectiveSegment == Segment->Image.VirtualAddress) 2907 { 2908 LastSegment ++; 2909 ASSERT(LastSegment <= i); 2910 ASSERT(LastSegment < ImageSectionObject->NrSegments); 2911 2912 EffectiveSegment = &ImageSectionObject->Segments[LastSegment]; 2913 2914 if (LastSegment != i) 2915 { 2916 /* 2917 * Copy the current segment. If necessary, the effective segment 2918 * will be expanded later 2919 */ 2920 *EffectiveSegment = *Segment; 2921 } 2922 2923 /* 2924 * Page-align the virtual size. We know for sure the virtual address 2925 * already is 2926 */ 2927 ASSERT((EffectiveSegment->Image.VirtualAddress % PAGE_SIZE) == 0); 2928 EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(EffectiveSegment->Length.QuadPart); 2929 } 2930 /* 2931 * The current segment is still part of the current effective segment: 2932 * extend the effective segment to reflect this 2933 */ 2934 else if (EndOfEffectiveSegment > Segment->Image.VirtualAddress) 2935 { 2936 static const ULONG FlagsToProtection[16] = 2937 { 2938 PAGE_NOACCESS, 2939 PAGE_READONLY, 2940 PAGE_READWRITE, 2941 PAGE_READWRITE, 2942 PAGE_EXECUTE_READ, 2943 PAGE_EXECUTE_READ, 2944 PAGE_EXECUTE_READWRITE, 2945 PAGE_EXECUTE_READWRITE, 2946 PAGE_WRITECOPY, 2947 PAGE_WRITECOPY, 2948 PAGE_WRITECOPY, 2949 PAGE_WRITECOPY, 2950 PAGE_EXECUTE_WRITECOPY, 2951 PAGE_EXECUTE_WRITECOPY, 2952 PAGE_EXECUTE_WRITECOPY, 2953 PAGE_EXECUTE_WRITECOPY 2954 }; 2955 2956 unsigned ProtectionFlags; 2957 2958 /* 2959 * Extend the file size 2960 */ 2961 2962 /* Unaligned segments must be contiguous within the file */ 2963 if (Segment->Image.FileOffset != (EffectiveSegment->Image.FileOffset + 2964 EffectiveSegment->RawLength.QuadPart)) 2965 { 2966 return FALSE; 2967 } 2968 2969 EffectiveSegment->RawLength.QuadPart += Segment->RawLength.QuadPart; 2970 2971 /* 2972 * Extend the virtual size 2973 */ 2974 ASSERT(PAGE_ROUND_UP(Segment->Image.VirtualAddress + Segment->Length.QuadPart) >= EndOfEffectiveSegment); 2975 2976 EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(Segment->Image.VirtualAddress + Segment->Length.QuadPart) - 2977 EffectiveSegment->Image.VirtualAddress; 2978 2979 /* 2980 * Merge the protection 2981 */ 2982 EffectiveSegment->Protection |= Segment->Protection; 2983 2984 /* Clean up redundance */ 2985 ProtectionFlags = 0; 2986 2987 if(EffectiveSegment->Protection & PAGE_IS_READABLE) 2988 ProtectionFlags |= 1 << 0; 2989 2990 if(EffectiveSegment->Protection & PAGE_IS_WRITABLE) 2991 ProtectionFlags |= 1 << 1; 2992 2993 if(EffectiveSegment->Protection & PAGE_IS_EXECUTABLE) 2994 ProtectionFlags |= 1 << 2; 2995 2996 if(EffectiveSegment->Protection & PAGE_IS_WRITECOPY) 2997 ProtectionFlags |= 1 << 3; 2998 2999 ASSERT(ProtectionFlags < 16); 3000 EffectiveSegment->Protection = FlagsToProtection[ProtectionFlags]; 3001 3002 /* If a segment was required to be shared and cannot, fail */ 3003 if(!(Segment->Protection & PAGE_IS_WRITECOPY) && 3004 EffectiveSegment->Protection & PAGE_IS_WRITECOPY) 3005 { 3006 return FALSE; 3007 } 3008 } 3009 /* 3010 * We assume no holes between segments at this point 3011 */ 3012 else 3013 { 3014 KeBugCheck(MEMORY_MANAGEMENT); 3015 } 3016 } 3017 } 3018 ImageSectionObject->NrSegments = LastSegment + 1; 3019 3020 return TRUE; 3021 } 3022 3023 NTSTATUS 3024 ExeFmtpCreateImageSection(PFILE_OBJECT FileObject, 3025 PMM_IMAGE_SECTION_OBJECT ImageSectionObject) 3026 { 3027 LARGE_INTEGER Offset; 3028 PVOID FileHeader; 3029 PVOID FileHeaderBuffer; 3030 ULONG FileHeaderSize; 3031 ULONG Flags; 3032 ULONG OldNrSegments; 3033 NTSTATUS Status; 3034 ULONG i; 3035 3036 /* 3037 * Read the beginning of the file (2 pages). Should be enough to contain 3038 * all (or most) of the headers 3039 */ 3040 Offset.QuadPart = 0; 3041 3042 Status = ExeFmtpReadFile (FileObject, 3043 &Offset, 3044 PAGE_SIZE * 2, 3045 &FileHeader, 3046 &FileHeaderBuffer, 3047 &FileHeaderSize); 3048 3049 if (!NT_SUCCESS(Status)) 3050 return Status; 3051 3052 if (FileHeaderSize == 0) 3053 { 3054 ExFreePool(FileHeaderBuffer); 3055 return STATUS_UNSUCCESSFUL; 3056 } 3057 3058 /* 3059 * Look for a loader that can handle this executable 3060 */ 3061 for (i = 0; i < RTL_NUMBER_OF(ExeFmtpLoaders); ++ i) 3062 { 3063 Flags = 0; 3064 3065 Status = ExeFmtpLoaders[i](FileHeader, 3066 FileHeaderSize, 3067 FileObject, 3068 ImageSectionObject, 3069 &Flags, 3070 ExeFmtpReadFile, 3071 ExeFmtpAllocateSegments); 3072 3073 if (!NT_SUCCESS(Status)) 3074 { 3075 if (ImageSectionObject->Segments) 3076 { 3077 ExFreePool(ImageSectionObject->Segments); 3078 ImageSectionObject->Segments = NULL; 3079 } 3080 } 3081 3082 if (Status != STATUS_ROS_EXEFMT_UNKNOWN_FORMAT) 3083 break; 3084 } 3085 3086 ExFreePoolWithTag(FileHeaderBuffer, 'rXmM'); 3087 3088 /* 3089 * No loader handled the format 3090 */ 3091 if (Status == STATUS_ROS_EXEFMT_UNKNOWN_FORMAT) 3092 { 3093 Status = STATUS_INVALID_IMAGE_NOT_MZ; 3094 ASSERT(!NT_SUCCESS(Status)); 3095 } 3096 3097 if (!NT_SUCCESS(Status)) 3098 return Status; 3099 3100 ASSERT(ImageSectionObject->Segments != NULL); 3101 ASSERT(ImageSectionObject->RefCount > 0); 3102 3103 /* 3104 * Some defaults 3105 */ 3106 /* FIXME? are these values platform-dependent? */ 3107 if (ImageSectionObject->ImageInformation.MaximumStackSize == 0) 3108 ImageSectionObject->ImageInformation.MaximumStackSize = 0x40000; 3109 3110 if(ImageSectionObject->ImageInformation.CommittedStackSize == 0) 3111 ImageSectionObject->ImageInformation.CommittedStackSize = 0x1000; 3112 3113 if(ImageSectionObject->BasedAddress == NULL) 3114 { 3115 if(ImageSectionObject->ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL) 3116 ImageSectionObject->BasedAddress = (PVOID)0x10000000; 3117 else 3118 ImageSectionObject->BasedAddress = (PVOID)0x00400000; 3119 } 3120 3121 /* 3122 * And now the fun part: fixing the segments 3123 */ 3124 3125 /* Sort them by virtual address */ 3126 MmspSortSegments(ImageSectionObject, Flags); 3127 3128 /* Ensure they don't overlap in memory */ 3129 if (!MmspCheckSegmentBounds(ImageSectionObject, Flags)) 3130 return STATUS_INVALID_IMAGE_FORMAT; 3131 3132 /* Ensure they are aligned */ 3133 OldNrSegments = ImageSectionObject->NrSegments; 3134 3135 if (!MmspPageAlignSegments(ImageSectionObject, Flags)) 3136 return STATUS_INVALID_IMAGE_FORMAT; 3137 3138 /* Trim them if the alignment phase merged some of them */ 3139 if (ImageSectionObject->NrSegments < OldNrSegments) 3140 { 3141 PMM_SECTION_SEGMENT Segments; 3142 SIZE_T SizeOfSegments; 3143 3144 SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * ImageSectionObject->NrSegments; 3145 3146 Segments = ExAllocatePoolWithTag(PagedPool, 3147 SizeOfSegments, 3148 TAG_MM_SECTION_SEGMENT); 3149 3150 if (Segments == NULL) 3151 return STATUS_INSUFFICIENT_RESOURCES; 3152 3153 RtlCopyMemory(Segments, ImageSectionObject->Segments, SizeOfSegments); 3154 ExFreePool(ImageSectionObject->Segments); 3155 ImageSectionObject->Segments = Segments; 3156 } 3157 3158 /* And finish their initialization */ 3159 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i ) 3160 { 3161 ExInitializeFastMutex(&ImageSectionObject->Segments[i].Lock); 3162 ImageSectionObject->Segments[i].ReferenceCount = &ImageSectionObject->RefCount; 3163 ImageSectionObject->Segments[i].Flags = &ImageSectionObject->SegFlags; 3164 MiInitializeSectionPageTable(&ImageSectionObject->Segments[i]); 3165 ImageSectionObject->Segments[i].FileObject = FileObject; 3166 } 3167 3168 ASSERT(ImageSectionObject->RefCount > 0); 3169 3170 ImageSectionObject->FileObject = FileObject; 3171 3172 ASSERT(NT_SUCCESS(Status)); 3173 return Status; 3174 } 3175 3176 NTSTATUS 3177 MmCreateImageSection(PSECTION *SectionObject, 3178 ACCESS_MASK DesiredAccess, 3179 POBJECT_ATTRIBUTES ObjectAttributes, 3180 PLARGE_INTEGER UMaximumSize, 3181 ULONG SectionPageProtection, 3182 ULONG AllocationAttributes, 3183 PFILE_OBJECT FileObject) 3184 { 3185 PSECTION Section; 3186 NTSTATUS Status; 3187 PMM_IMAGE_SECTION_OBJECT ImageSectionObject; 3188 KIRQL OldIrql; 3189 3190 3191 if (FileObject == NULL) 3192 return STATUS_INVALID_FILE_FOR_SECTION; 3193 3194 if (FileObject->SectionObjectPointer == NULL) 3195 { 3196 DPRINT1("Denying section creation due to missing cache initialization\n"); 3197 return STATUS_INVALID_FILE_FOR_SECTION; 3198 } 3199 3200 /* 3201 * Create the section 3202 */ 3203 Status = ObCreateObject (ExGetPreviousMode(), 3204 MmSectionObjectType, 3205 ObjectAttributes, 3206 ExGetPreviousMode(), 3207 NULL, 3208 sizeof(*Section), 3209 0, 3210 0, 3211 (PVOID*)(PVOID)&Section); 3212 if (!NT_SUCCESS(Status)) 3213 { 3214 return Status; 3215 } 3216 3217 /* 3218 * Initialize it 3219 */ 3220 RtlZeroMemory(Section, sizeof(*Section)); 3221 3222 /* Mark this as a "ROS" Section */ 3223 Section->u.Flags.filler = 1; 3224 3225 Section->InitialPageProtection = SectionPageProtection; 3226 Section->u.Flags.File = 1; 3227 Section->u.Flags.Image = 1; 3228 if (AllocationAttributes & SEC_NO_CHANGE) 3229 Section->u.Flags.NoChange = 1; 3230 3231 grab_image_section_object: 3232 OldIrql = MiAcquirePfnLock(); 3233 3234 /* Wait for it to be properly created or deleted */ 3235 ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject; 3236 while(ImageSectionObject && (ImageSectionObject->SegFlags & (MM_SEGMENT_INDELETE | MM_SEGMENT_INCREATE))) 3237 { 3238 MiReleasePfnLock(OldIrql); 3239 3240 KeDelayExecutionThread(KernelMode, FALSE, &TinyTime); 3241 3242 OldIrql = MiAcquirePfnLock(); 3243 ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject; 3244 } 3245 3246 if (ImageSectionObject == NULL) 3247 { 3248 NTSTATUS StatusExeFmt; 3249 3250 /* Release the lock because ExAllocatePoolWithTag could need to acquire it */ 3251 MiReleasePfnLock(OldIrql); 3252 3253 ImageSectionObject = ExAllocatePoolZero(NonPagedPool, sizeof(MM_IMAGE_SECTION_OBJECT), TAG_MM_SECTION_SEGMENT); 3254 if (ImageSectionObject == NULL) 3255 { 3256 ObDereferenceObject(Section); 3257 return STATUS_NO_MEMORY; 3258 } 3259 3260 ImageSectionObject->SegFlags = MM_SEGMENT_INCREATE; 3261 ImageSectionObject->RefCount = 1; 3262 ImageSectionObject->SectionCount = 1; 3263 3264 OldIrql = MiAcquirePfnLock(); 3265 if (FileObject->SectionObjectPointer->ImageSectionObject != NULL) 3266 { 3267 MiReleasePfnLock(OldIrql); 3268 /* Bad luck. Start over */ 3269 ExFreePoolWithTag(ImageSectionObject, TAG_MM_SECTION_SEGMENT); 3270 goto grab_image_section_object; 3271 } 3272 3273 FileObject->SectionObjectPointer->ImageSectionObject = ImageSectionObject; 3274 3275 MiReleasePfnLock(OldIrql); 3276 3277 /* Purge the cache */ 3278 CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, NULL); 3279 3280 StatusExeFmt = ExeFmtpCreateImageSection(FileObject, ImageSectionObject); 3281 3282 if (!NT_SUCCESS(StatusExeFmt)) 3283 { 3284 /* Unset */ 3285 OldIrql = MiAcquirePfnLock(); 3286 FileObject->SectionObjectPointer->ImageSectionObject = NULL; 3287 MiReleasePfnLock(OldIrql); 3288 3289 if(ImageSectionObject->Segments != NULL) 3290 ExFreePool(ImageSectionObject->Segments); 3291 3292 /* 3293 * If image file is empty, then return that the file is invalid for section 3294 */ 3295 Status = StatusExeFmt; 3296 if (StatusExeFmt == STATUS_END_OF_FILE) 3297 { 3298 Status = STATUS_INVALID_FILE_FOR_SECTION; 3299 } 3300 3301 ExFreePoolWithTag(ImageSectionObject, TAG_MM_SECTION_SEGMENT); 3302 ObDereferenceObject(Section); 3303 return Status; 3304 } 3305 3306 Section->Segment = (PSEGMENT)ImageSectionObject; 3307 ASSERT(ImageSectionObject->Segments); 3308 ASSERT(ImageSectionObject->RefCount > 0); 3309 3310 /* 3311 * Lock the file 3312 */ 3313 Status = MmspWaitForFileLock(FileObject); 3314 if (!NT_SUCCESS(Status)) 3315 { 3316 /* Unset */ 3317 OldIrql = MiAcquirePfnLock(); 3318 FileObject->SectionObjectPointer->ImageSectionObject = NULL; 3319 MiReleasePfnLock(OldIrql); 3320 3321 ExFreePool(ImageSectionObject->Segments); 3322 ExFreePool(ImageSectionObject); 3323 ObDereferenceObject(Section); 3324 return Status; 3325 } 3326 3327 OldIrql = MiAcquirePfnLock(); 3328 ImageSectionObject->SegFlags &= ~MM_SEGMENT_INCREATE; 3329 3330 /* Take a ref on the file on behalf of the newly created structure */ 3331 ObReferenceObject(FileObject); 3332 3333 MiReleasePfnLock(OldIrql); 3334 3335 Status = StatusExeFmt; 3336 } 3337 else 3338 { 3339 /* If FS driver called for delete, tell them it's not possible anymore. */ 3340 ImageSectionObject->SegFlags &= ~MM_IMAGE_SECTION_FLUSH_DELETE; 3341 3342 /* Take one ref */ 3343 InterlockedIncrement64(&ImageSectionObject->RefCount); 3344 ImageSectionObject->SectionCount++; 3345 3346 MiReleasePfnLock(OldIrql); 3347 3348 Section->Segment = (PSEGMENT)ImageSectionObject; 3349 3350 Status = STATUS_SUCCESS; 3351 } 3352 //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE); 3353 *SectionObject = Section; 3354 ASSERT(ImageSectionObject->RefCount > 0); 3355 3356 return Status; 3357 } 3358 3359 3360 3361 static NTSTATUS 3362 MmMapViewOfSegment( 3363 PMMSUPPORT AddressSpace, 3364 BOOLEAN AsImage, 3365 PMM_SECTION_SEGMENT Segment, 3366 PVOID* BaseAddress, 3367 SIZE_T ViewSize, 3368 ULONG Protect, 3369 LONGLONG ViewOffset, 3370 ULONG AllocationType) 3371 { 3372 PMEMORY_AREA MArea; 3373 NTSTATUS Status; 3374 ULONG Granularity; 3375 3376 ASSERT(ViewSize != 0); 3377 3378 if (Segment->WriteCopy) 3379 { 3380 /* We have to do this because the not present fault 3381 * and access fault handlers depend on the protection 3382 * that should be granted AFTER the COW fault takes 3383 * place to be in Region->Protect. The not present fault 3384 * handler changes this to the correct protection for COW when 3385 * mapping the pages into the process's address space. If a COW 3386 * fault takes place, the access fault handler sets the page protection 3387 * to these values for the newly copied pages 3388 */ 3389 if (Protect == PAGE_WRITECOPY) 3390 Protect = PAGE_READWRITE; 3391 else if (Protect == PAGE_EXECUTE_WRITECOPY) 3392 Protect = PAGE_EXECUTE_READWRITE; 3393 } 3394 3395 if (*BaseAddress == NULL) 3396 Granularity = MM_ALLOCATION_GRANULARITY; 3397 else 3398 Granularity = PAGE_SIZE; 3399 3400 #ifdef NEWCC 3401 if (Segment->Flags & MM_DATAFILE_SEGMENT) 3402 { 3403 LARGE_INTEGER FileOffset; 3404 FileOffset.QuadPart = ViewOffset; 3405 ObReferenceObject(Section); 3406 return _MiMapViewOfSegment(AddressSpace, Segment, BaseAddress, ViewSize, Protect, &FileOffset, AllocationType, __FILE__, __LINE__); 3407 } 3408 #endif 3409 Status = MmCreateMemoryArea(AddressSpace, 3410 MEMORY_AREA_SECTION_VIEW, 3411 BaseAddress, 3412 ViewSize, 3413 Protect, 3414 &MArea, 3415 AllocationType, 3416 Granularity); 3417 if (!NT_SUCCESS(Status)) 3418 { 3419 DPRINT1("Mapping between 0x%p and 0x%p failed (%X).\n", 3420 (*BaseAddress), (char*)(*BaseAddress) + ViewSize, Status); 3421 return Status; 3422 } 3423 3424 InterlockedIncrement64(Segment->ReferenceCount); 3425 3426 MArea->SectionData.Segment = Segment; 3427 MArea->SectionData.ViewOffset = ViewOffset; 3428 if (AsImage) 3429 { 3430 MArea->VadNode.u.VadFlags.VadType = VadImageMap; 3431 } 3432 3433 MmInitializeRegion(&MArea->SectionData.RegionListHead, 3434 ViewSize, 0, Protect); 3435 3436 return STATUS_SUCCESS; 3437 } 3438 3439 3440 static VOID 3441 MmFreeSectionPage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address, 3442 PFN_NUMBER Page, SWAPENTRY SwapEntry, BOOLEAN Dirty) 3443 { 3444 ULONG_PTR Entry; 3445 LARGE_INTEGER Offset; 3446 SWAPENTRY SavedSwapEntry; 3447 PMM_SECTION_SEGMENT Segment; 3448 PMMSUPPORT AddressSpace; 3449 PEPROCESS Process; 3450 3451 AddressSpace = (PMMSUPPORT)Context; 3452 Process = MmGetAddressSpaceOwner(AddressSpace); 3453 3454 Address = (PVOID)PAGE_ROUND_DOWN(Address); 3455 3456 Offset.QuadPart = ((ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea)) + 3457 MemoryArea->SectionData.ViewOffset; 3458 3459 Segment = MemoryArea->SectionData.Segment; 3460 3461 Entry = MmGetPageEntrySectionSegment(Segment, &Offset); 3462 while (Entry && MM_IS_WAIT_PTE(Entry)) 3463 { 3464 MmUnlockSectionSegment(Segment); 3465 MmUnlockAddressSpace(AddressSpace); 3466 3467 KeDelayExecutionThread(KernelMode, FALSE, &TinyTime); 3468 3469 MmLockAddressSpace(AddressSpace); 3470 MmLockSectionSegment(Segment); 3471 Entry = MmGetPageEntrySectionSegment(Segment, &Offset); 3472 } 3473 3474 /* 3475 * For a dirty, datafile, non-private page, there shoulkd be no swap entry 3476 */ 3477 if (*Segment->Flags & MM_DATAFILE_SEGMENT) 3478 { 3479 if (Page == PFN_FROM_SSE(Entry) && Dirty) 3480 { 3481 ASSERT(SwapEntry == 0); 3482 } 3483 } 3484 3485 if (SwapEntry != 0) 3486 { 3487 /* 3488 * Sanity check 3489 */ 3490 MmFreeSwapPage(SwapEntry); 3491 } 3492 else if (Page != 0) 3493 { 3494 if (IS_SWAP_FROM_SSE(Entry) || 3495 Page != PFN_FROM_SSE(Entry)) 3496 { 3497 ASSERT(Process != NULL); 3498 3499 /* 3500 * Just dereference private pages 3501 */ 3502 SavedSwapEntry = MmGetSavedSwapEntryPage(Page); 3503 if (SavedSwapEntry != 0) 3504 { 3505 MmFreeSwapPage(SavedSwapEntry); 3506 MmSetSavedSwapEntryPage(Page, 0); 3507 } 3508 MmDeleteRmap(Page, Process, Address); 3509 MmReleasePageMemoryConsumer(MC_USER, Page); 3510 } 3511 else 3512 { 3513 if (Process) 3514 { 3515 MmDeleteRmap(Page, Process, Address); 3516 } 3517 3518 /* We don't dirtify for System Space Maps. We let Cc manage that */ 3519 MmUnsharePageEntrySectionSegment(MemoryArea, Segment, &Offset, Process ? Dirty : FALSE, FALSE, NULL); 3520 } 3521 } 3522 } 3523 3524 static NTSTATUS 3525 MmUnmapViewOfSegment(PMMSUPPORT AddressSpace, 3526 PVOID BaseAddress) 3527 { 3528 NTSTATUS Status; 3529 PMEMORY_AREA MemoryArea; 3530 PMM_SECTION_SEGMENT Segment; 3531 PLIST_ENTRY CurrentEntry; 3532 PMM_REGION CurrentRegion; 3533 PLIST_ENTRY RegionListHead; 3534 3535 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, 3536 BaseAddress); 3537 if (MemoryArea == NULL) 3538 { 3539 return STATUS_UNSUCCESSFUL; 3540 } 3541 3542 Segment = MemoryArea->SectionData.Segment; 3543 3544 #ifdef NEWCC 3545 if (Segment->Flags & MM_DATAFILE_SEGMENT) 3546 { 3547 MmUnlockAddressSpace(AddressSpace); 3548 Status = MmUnmapViewOfCacheSegment(AddressSpace, BaseAddress); 3549 MmLockAddressSpace(AddressSpace); 3550 3551 return Status; 3552 } 3553 #endif 3554 3555 MemoryArea->DeleteInProgress = TRUE; 3556 3557 MmLockSectionSegment(Segment); 3558 3559 RegionListHead = &MemoryArea->SectionData.RegionListHead; 3560 while (!IsListEmpty(RegionListHead)) 3561 { 3562 CurrentEntry = RemoveHeadList(RegionListHead); 3563 CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION, RegionListEntry); 3564 ExFreePoolWithTag(CurrentRegion, TAG_MM_REGION); 3565 } 3566 3567 if ((*Segment->Flags) & MM_PHYSICALMEMORY_SEGMENT) 3568 { 3569 Status = MmFreeMemoryArea(AddressSpace, 3570 MemoryArea, 3571 NULL, 3572 NULL); 3573 } 3574 else 3575 { 3576 Status = MmFreeMemoryArea(AddressSpace, 3577 MemoryArea, 3578 MmFreeSectionPage, 3579 AddressSpace); 3580 } 3581 MmUnlockSectionSegment(Segment); 3582 MmDereferenceSegment(Segment); 3583 return Status; 3584 } 3585 3586 /* This functions must be called with a locked address space */ 3587 NTSTATUS 3588 NTAPI 3589 MiRosUnmapViewOfSection(IN PEPROCESS Process, 3590 IN PVOID BaseAddress, 3591 IN BOOLEAN SkipDebuggerNotify) 3592 { 3593 NTSTATUS Status; 3594 PMEMORY_AREA MemoryArea; 3595 PMMSUPPORT AddressSpace; 3596 PVOID ImageBaseAddress = 0; 3597 3598 DPRINT("Opening memory area Process %p BaseAddress %p\n", 3599 Process, BaseAddress); 3600 3601 ASSERT(Process); 3602 3603 AddressSpace = &Process->Vm; 3604 3605 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, 3606 BaseAddress); 3607 if (MemoryArea == NULL || 3608 #ifdef NEWCC 3609 ((MemoryArea->Type != MEMORY_AREA_SECTION_VIEW) && (MemoryArea->Type != MEMORY_AREA_CACHE)) || 3610 #else 3611 (MemoryArea->Type != MEMORY_AREA_SECTION_VIEW) || 3612 #endif 3613 MemoryArea->DeleteInProgress) 3614 3615 { 3616 if (MemoryArea) ASSERT(MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3); 3617 3618 DPRINT1("Unable to find memory area at address %p.\n", BaseAddress); 3619 return STATUS_NOT_MAPPED_VIEW; 3620 } 3621 3622 if (MemoryArea->VadNode.u.VadFlags.VadType == VadImageMap) 3623 { 3624 ULONG i; 3625 ULONG NrSegments; 3626 PMM_IMAGE_SECTION_OBJECT ImageSectionObject; 3627 PMM_SECTION_SEGMENT SectionSegments; 3628 PMM_SECTION_SEGMENT Segment; 3629 3630 Segment = MemoryArea->SectionData.Segment; 3631 ImageSectionObject = ImageSectionObjectFromSegment(Segment); 3632 SectionSegments = ImageSectionObject->Segments; 3633 NrSegments = ImageSectionObject->NrSegments; 3634 3635 MemoryArea->DeleteInProgress = TRUE; 3636 3637 /* Search for the current segment within the section segments 3638 * and calculate the image base address */ 3639 for (i = 0; i < NrSegments; i++) 3640 { 3641 if (Segment == &SectionSegments[i]) 3642 { 3643 ImageBaseAddress = (char*)BaseAddress - (ULONG_PTR)SectionSegments[i].Image.VirtualAddress; 3644 break; 3645 } 3646 } 3647 if (i >= NrSegments) 3648 { 3649 KeBugCheck(MEMORY_MANAGEMENT); 3650 } 3651 3652 for (i = 0; i < NrSegments; i++) 3653 { 3654 PVOID SBaseAddress = (PVOID) 3655 ((char*)ImageBaseAddress + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress); 3656 3657 Status = MmUnmapViewOfSegment(AddressSpace, SBaseAddress); 3658 if (!NT_SUCCESS(Status)) 3659 { 3660 DPRINT1("MmUnmapViewOfSegment failed for %p (Process %p) with %lx\n", 3661 SBaseAddress, Process, Status); 3662 ASSERT(NT_SUCCESS(Status)); 3663 } 3664 } 3665 DPRINT("One mapping less for %p\n", ImageSectionObject->FileObject->SectionObjectPointer); 3666 InterlockedDecrement(&ImageSectionObject->MapCount); 3667 } 3668 else 3669 { 3670 PMM_SECTION_SEGMENT Segment = MemoryArea->SectionData.Segment; 3671 PMMVAD Vad = &MemoryArea->VadNode; 3672 PCONTROL_AREA ControlArea = Vad->ControlArea; 3673 PFILE_OBJECT FileObject; 3674 SIZE_T ViewSize; 3675 LARGE_INTEGER ViewOffset; 3676 ViewOffset.QuadPart = MemoryArea->SectionData.ViewOffset; 3677 3678 InterlockedIncrement64(Segment->ReferenceCount); 3679 3680 ViewSize = PAGE_SIZE + ((Vad->EndingVpn - Vad->StartingVpn) << PAGE_SHIFT); 3681 3682 Status = MmUnmapViewOfSegment(AddressSpace, BaseAddress); 3683 if (!NT_SUCCESS(Status)) 3684 { 3685 DPRINT1("MmUnmapViewOfSegment failed for %p (Process %p) with %lx\n", 3686 BaseAddress, Process, Status); 3687 ASSERT(NT_SUCCESS(Status)); 3688 } 3689 3690 /* These might be deleted now */ 3691 Vad = NULL; 3692 MemoryArea = NULL; 3693 3694 if (FlagOn(*Segment->Flags, MM_PHYSICALMEMORY_SEGMENT)) 3695 { 3696 /* Don't bother */ 3697 MmDereferenceSegment(Segment); 3698 return STATUS_SUCCESS; 3699 } 3700 ASSERT(FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT)); 3701 3702 FileObject = Segment->FileObject; 3703 FsRtlAcquireFileExclusive(FileObject); 3704 3705 /* Don't bother for auto-delete closed file. */ 3706 if (FlagOn(FileObject->Flags, FO_DELETE_ON_CLOSE) && FlagOn(FileObject->Flags, FO_CLEANUP_COMPLETE)) 3707 { 3708 FsRtlReleaseFile(FileObject); 3709 MmDereferenceSegment(Segment); 3710 return STATUS_SUCCESS; 3711 } 3712 3713 /* 3714 * Flush only when last mapping is deleted. 3715 * FIXME: Why ControlArea == NULL? Or rather: is ControlArea ever not NULL here? 3716 */ 3717 if (ControlArea == NULL || ControlArea->NumberOfMappedViews == 1) 3718 { 3719 while (ViewSize > 0) 3720 { 3721 ULONG FlushSize = min(ViewSize, PAGE_ROUND_DOWN(MAXULONG)); 3722 MmFlushSegment(FileObject->SectionObjectPointer, 3723 &ViewOffset, 3724 FlushSize, 3725 NULL); 3726 ViewSize -= FlushSize; 3727 ViewOffset.QuadPart += FlushSize; 3728 } 3729 } 3730 3731 FsRtlReleaseFile(FileObject); 3732 MmDereferenceSegment(Segment); 3733 } 3734 3735 /* Notify debugger */ 3736 if (ImageBaseAddress && !SkipDebuggerNotify) DbgkUnMapViewOfSection(ImageBaseAddress); 3737 3738 return STATUS_SUCCESS; 3739 } 3740 3741 3742 3743 3744 /** 3745 * Queries the information of a section object. 3746 * 3747 * @param SectionHandle 3748 * Handle to the section object. It must be opened with SECTION_QUERY 3749 * access. 3750 * @param SectionInformationClass 3751 * Index to a certain information structure. Can be either 3752 * SectionBasicInformation or SectionImageInformation. The latter 3753 * is valid only for sections that were created with the SEC_IMAGE 3754 * flag. 3755 * @param SectionInformation 3756 * Caller supplies storage for resulting information. 3757 * @param Length 3758 * Size of the supplied storage. 3759 * @param ResultLength 3760 * Data written. 3761 * 3762 * @return Status. 3763 * 3764 * @implemented 3765 */ 3766 NTSTATUS 3767 NTAPI 3768 NtQuerySection( 3769 _In_ HANDLE SectionHandle, 3770 _In_ SECTION_INFORMATION_CLASS SectionInformationClass, 3771 _Out_ PVOID SectionInformation, 3772 _In_ SIZE_T SectionInformationLength, 3773 _Out_opt_ PSIZE_T ResultLength) 3774 { 3775 PSECTION Section; 3776 KPROCESSOR_MODE PreviousMode; 3777 NTSTATUS Status; 3778 PAGED_CODE(); 3779 3780 PreviousMode = ExGetPreviousMode(); 3781 if (PreviousMode != KernelMode) 3782 { 3783 _SEH2_TRY 3784 { 3785 ProbeForWrite(SectionInformation, 3786 SectionInformationLength, 3787 __alignof(ULONG)); 3788 if (ResultLength != NULL) 3789 { 3790 ProbeForWrite(ResultLength, 3791 sizeof(*ResultLength), 3792 __alignof(SIZE_T)); 3793 } 3794 } 3795 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3796 { 3797 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 3798 } 3799 _SEH2_END; 3800 } 3801 3802 if (SectionInformationClass == SectionBasicInformation) 3803 { 3804 if (SectionInformationLength < sizeof(SECTION_BASIC_INFORMATION)) 3805 { 3806 return STATUS_INFO_LENGTH_MISMATCH; 3807 } 3808 } 3809 else if (SectionInformationClass == SectionImageInformation) 3810 { 3811 if (SectionInformationLength < sizeof(SECTION_IMAGE_INFORMATION)) 3812 { 3813 return STATUS_INFO_LENGTH_MISMATCH; 3814 } 3815 } 3816 else 3817 { 3818 return STATUS_INVALID_INFO_CLASS; 3819 } 3820 3821 Status = ObReferenceObjectByHandle(SectionHandle, 3822 SECTION_QUERY, 3823 MmSectionObjectType, 3824 PreviousMode, 3825 (PVOID*)(PVOID)&Section, 3826 NULL); 3827 if (!NT_SUCCESS(Status)) 3828 { 3829 DPRINT1("Failed to reference section: 0x%lx\n", Status); 3830 return Status; 3831 } 3832 3833 switch(SectionInformationClass) 3834 { 3835 case SectionBasicInformation: 3836 { 3837 SECTION_BASIC_INFORMATION Sbi; 3838 3839 Sbi.Size = Section->SizeOfSection; 3840 Sbi.BaseAddress = (PVOID)Section->Address.StartingVpn; 3841 3842 Sbi.Attributes = 0; 3843 if (Section->u.Flags.File) 3844 Sbi.Attributes |= SEC_FILE; 3845 if (Section->u.Flags.Image) 3846 Sbi.Attributes |= SEC_IMAGE; 3847 3848 /* Those are not set ************* 3849 if (Section->u.Flags.Commit) 3850 Sbi.Attributes |= SEC_COMMIT; 3851 if (Section->u.Flags.Reserve) 3852 Sbi.Attributes |= SEC_RESERVE; 3853 **********************************/ 3854 3855 if (Section->u.Flags.Image) 3856 { 3857 if (MiIsRosSectionObject(Section)) 3858 { 3859 PMM_IMAGE_SECTION_OBJECT ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment); 3860 Sbi.BaseAddress = 0; 3861 Sbi.Size.QuadPart = ImageSectionObject->ImageInformation.ImageFileSize; 3862 } 3863 else 3864 { 3865 /* Not supported yet */ 3866 ASSERT(FALSE); 3867 } 3868 } 3869 else if (MiIsRosSectionObject(Section)) 3870 { 3871 Sbi.BaseAddress = (PVOID)((PMM_SECTION_SEGMENT)Section->Segment)->Image.VirtualAddress; 3872 Sbi.Size.QuadPart = ((PMM_SECTION_SEGMENT)Section->Segment)->RawLength.QuadPart; 3873 } 3874 else 3875 { 3876 DPRINT1("Unimplemented code path\n"); 3877 } 3878 3879 _SEH2_TRY 3880 { 3881 *((SECTION_BASIC_INFORMATION*)SectionInformation) = Sbi; 3882 if (ResultLength != NULL) 3883 { 3884 *ResultLength = sizeof(Sbi); 3885 } 3886 } 3887 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3888 { 3889 Status = _SEH2_GetExceptionCode(); 3890 } 3891 _SEH2_END; 3892 break; 3893 } 3894 case SectionImageInformation: 3895 { 3896 if (!Section->u.Flags.Image) 3897 { 3898 Status = STATUS_SECTION_NOT_IMAGE; 3899 } 3900 else if (MiIsRosSectionObject(Section)) 3901 { 3902 PMM_IMAGE_SECTION_OBJECT ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment); 3903 3904 _SEH2_TRY 3905 { 3906 PSECTION_IMAGE_INFORMATION Sii = (PSECTION_IMAGE_INFORMATION)SectionInformation; 3907 *Sii = ImageSectionObject->ImageInformation; 3908 if (ResultLength != NULL) 3909 { 3910 *ResultLength = sizeof(*Sii); 3911 } 3912 } 3913 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3914 { 3915 Status = _SEH2_GetExceptionCode(); 3916 } 3917 _SEH2_END; 3918 } 3919 else 3920 { 3921 _SEH2_TRY 3922 { 3923 PSECTION_IMAGE_INFORMATION Sii = (PSECTION_IMAGE_INFORMATION)SectionInformation; 3924 *Sii = *Section->Segment->u2.ImageInformation; 3925 if (ResultLength != NULL) 3926 *ResultLength = sizeof(*Sii); 3927 } 3928 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3929 { 3930 Status = _SEH2_GetExceptionCode(); 3931 } 3932 _SEH2_END; 3933 } 3934 break; 3935 } 3936 default: 3937 DPRINT1("Unknown SectionInformationClass: %d\n", SectionInformationClass); 3938 Status = STATUS_NOT_SUPPORTED; 3939 } 3940 3941 ObDereferenceObject(Section); 3942 3943 return Status; 3944 } 3945 3946 /********************************************************************** 3947 * NAME EXPORTED 3948 * MmMapViewOfSection 3949 * 3950 * DESCRIPTION 3951 * Maps a view of a section into the virtual address space of a 3952 * process. 3953 * 3954 * ARGUMENTS 3955 * Section 3956 * Pointer to the section object. 3957 * 3958 * ProcessHandle 3959 * Pointer to the process. 3960 * 3961 * BaseAddress 3962 * Desired base address (or NULL) on entry; 3963 * Actual base address of the view on exit. 3964 * 3965 * ZeroBits 3966 * Number of high order address bits that must be zero. 3967 * 3968 * CommitSize 3969 * Size in bytes of the initially committed section of 3970 * the view. 3971 * 3972 * SectionOffset 3973 * Offset in bytes from the beginning of the section 3974 * to the beginning of the view. 3975 * 3976 * ViewSize 3977 * Desired length of map (or zero to map all) on entry 3978 * Actual length mapped on exit. 3979 * 3980 * InheritDisposition 3981 * Specified how the view is to be shared with 3982 * child processes. 3983 * 3984 * AllocationType 3985 * Type of allocation for the pages. 3986 * 3987 * Protect 3988 * Protection for the committed region of the view. 3989 * 3990 * RETURN VALUE 3991 * Status. 3992 * 3993 * @implemented 3994 */ 3995 NTSTATUS NTAPI 3996 MmMapViewOfSection(IN PVOID SectionObject, 3997 IN PEPROCESS Process, 3998 IN OUT PVOID *BaseAddress, 3999 IN ULONG_PTR ZeroBits, 4000 IN SIZE_T CommitSize, 4001 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL, 4002 IN OUT PSIZE_T ViewSize, 4003 IN SECTION_INHERIT InheritDisposition, 4004 IN ULONG AllocationType, 4005 IN ULONG Protect) 4006 { 4007 PSECTION Section; 4008 PMMSUPPORT AddressSpace; 4009 NTSTATUS Status = STATUS_SUCCESS; 4010 BOOLEAN NotAtBase = FALSE; 4011 BOOLEAN IsAttached = FALSE; 4012 KAPC_STATE ApcState; 4013 4014 if (MiIsRosSectionObject(SectionObject) == FALSE) 4015 { 4016 DPRINT("Mapping ARM3 section into %s\n", Process->ImageFileName); 4017 return MmMapViewOfArm3Section(SectionObject, 4018 Process, 4019 BaseAddress, 4020 ZeroBits, 4021 CommitSize, 4022 SectionOffset, 4023 ViewSize, 4024 InheritDisposition, 4025 AllocationType, 4026 Protect); 4027 } 4028 4029 ASSERT(Process); 4030 4031 if (!Protect || Protect & ~PAGE_FLAGS_VALID_FOR_SECTION) 4032 { 4033 return STATUS_INVALID_PAGE_PROTECTION; 4034 } 4035 4036 if (PsGetCurrentProcess() != Process) 4037 { 4038 KeStackAttachProcess(&Process->Pcb, &ApcState); 4039 IsAttached = TRUE; 4040 } 4041 4042 /* FIXME: We should keep this, but it would break code checking equality */ 4043 Protect &= ~PAGE_NOCACHE; 4044 4045 Section = SectionObject; 4046 AddressSpace = &Process->Vm; 4047 4048 if (Section->u.Flags.NoChange) 4049 AllocationType |= SEC_NO_CHANGE; 4050 4051 MmLockAddressSpace(AddressSpace); 4052 4053 if (Section->u.Flags.Image) 4054 { 4055 ULONG i; 4056 ULONG NrSegments; 4057 ULONG_PTR ImageBase; 4058 SIZE_T ImageSize; 4059 PMM_IMAGE_SECTION_OBJECT ImageSectionObject; 4060 PMM_SECTION_SEGMENT SectionSegments; 4061 4062 ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment); 4063 SectionSegments = ImageSectionObject->Segments; 4064 NrSegments = ImageSectionObject->NrSegments; 4065 4066 ASSERT(ImageSectionObject->RefCount > 0); 4067 4068 ImageBase = (ULONG_PTR)*BaseAddress; 4069 if (ImageBase == 0) 4070 { 4071 ImageBase = (ULONG_PTR)ImageSectionObject->BasedAddress; 4072 } 4073 4074 ImageSize = 0; 4075 for (i = 0; i < NrSegments; i++) 4076 { 4077 ULONG_PTR MaxExtent; 4078 MaxExtent = (ULONG_PTR)(SectionSegments[i].Image.VirtualAddress + 4079 SectionSegments[i].Length.QuadPart); 4080 ImageSize = max(ImageSize, MaxExtent); 4081 } 4082 4083 ImageSectionObject->ImageInformation.ImageFileSize = (ULONG)ImageSize; 4084 4085 /* Check for an illegal base address */ 4086 if (((ImageBase + ImageSize) > (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS) || 4087 ((ImageBase + ImageSize) < ImageSize)) 4088 { 4089 ASSERT(*BaseAddress == NULL); 4090 ImageBase = ALIGN_DOWN_BY((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - ImageSize, 4091 MM_VIRTMEM_GRANULARITY); 4092 NotAtBase = TRUE; 4093 } 4094 else if (ImageBase != ALIGN_DOWN_BY(ImageBase, MM_VIRTMEM_GRANULARITY)) 4095 { 4096 ASSERT(*BaseAddress == NULL); 4097 ImageBase = ALIGN_DOWN_BY(ImageBase, MM_VIRTMEM_GRANULARITY); 4098 NotAtBase = TRUE; 4099 } 4100 4101 /* Check there is enough space to map the section at that point. */ 4102 if (MmLocateMemoryAreaByRegion(AddressSpace, (PVOID)ImageBase, 4103 PAGE_ROUND_UP(ImageSize)) != NULL) 4104 { 4105 /* Fail if the user requested a fixed base address. */ 4106 if ((*BaseAddress) != NULL) 4107 { 4108 Status = STATUS_CONFLICTING_ADDRESSES; 4109 goto Exit; 4110 } 4111 /* Otherwise find a gap to map the image. */ 4112 ImageBase = (ULONG_PTR)MmFindGap(AddressSpace, PAGE_ROUND_UP(ImageSize), MM_VIRTMEM_GRANULARITY, FALSE); 4113 if (ImageBase == 0) 4114 { 4115 Status = STATUS_CONFLICTING_ADDRESSES; 4116 goto Exit; 4117 } 4118 /* Remember that we loaded image at a different base address */ 4119 NotAtBase = TRUE; 4120 } 4121 4122 for (i = 0; i < NrSegments; i++) 4123 { 4124 PVOID SBaseAddress = (PVOID) 4125 ((char*)ImageBase + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress); 4126 MmLockSectionSegment(&SectionSegments[i]); 4127 Status = MmMapViewOfSegment(AddressSpace, 4128 TRUE, 4129 &SectionSegments[i], 4130 &SBaseAddress, 4131 SectionSegments[i].Length.QuadPart, 4132 SectionSegments[i].Protection, 4133 0, 4134 0); 4135 MmUnlockSectionSegment(&SectionSegments[i]); 4136 if (!NT_SUCCESS(Status)) 4137 { 4138 /* roll-back */ 4139 while (i--) 4140 { 4141 SBaseAddress = ((char*)ImageBase + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress); 4142 MmLockSectionSegment(&SectionSegments[i]); 4143 MmUnmapViewOfSegment(AddressSpace, SBaseAddress); 4144 MmUnlockSectionSegment(&SectionSegments[i]); 4145 } 4146 4147 goto Exit; 4148 } 4149 } 4150 4151 *BaseAddress = (PVOID)ImageBase; 4152 *ViewSize = ImageSize; 4153 4154 DPRINT("Mapped %p for section pointer %p\n", ImageSectionObject, ImageSectionObject->FileObject->SectionObjectPointer); 4155 4156 /* One more map */ 4157 InterlockedIncrement(&ImageSectionObject->MapCount); 4158 } 4159 else 4160 { 4161 PMM_SECTION_SEGMENT Segment = (PMM_SECTION_SEGMENT)Section->Segment; 4162 LONGLONG ViewOffset; 4163 4164 ASSERT(Segment->RefCount > 0); 4165 4166 /* check for write access */ 4167 if ((Protect & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE)) && 4168 !(Section->InitialPageProtection & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE))) 4169 { 4170 Status = STATUS_SECTION_PROTECTION; 4171 goto Exit; 4172 } 4173 /* check for read access */ 4174 if ((Protect & (PAGE_READONLY|PAGE_WRITECOPY|PAGE_EXECUTE_READ|PAGE_EXECUTE_WRITECOPY)) && 4175 !(Section->InitialPageProtection & (PAGE_READONLY|PAGE_READWRITE|PAGE_WRITECOPY|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY))) 4176 { 4177 Status = STATUS_SECTION_PROTECTION; 4178 goto Exit; 4179 } 4180 /* check for execute access */ 4181 if ((Protect & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY)) && 4182 !(Section->InitialPageProtection & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY))) 4183 { 4184 Status = STATUS_SECTION_PROTECTION; 4185 goto Exit; 4186 } 4187 4188 if (SectionOffset == NULL) 4189 { 4190 ViewOffset = 0; 4191 } 4192 else 4193 { 4194 ViewOffset = SectionOffset->QuadPart; 4195 } 4196 4197 if ((ViewOffset % PAGE_SIZE) != 0) 4198 { 4199 Status = STATUS_MAPPED_ALIGNMENT; 4200 goto Exit; 4201 } 4202 4203 if ((*ViewSize) == 0) 4204 { 4205 (*ViewSize) = Section->SizeOfSection.QuadPart - ViewOffset; 4206 } 4207 else if ((ExGetPreviousMode() == UserMode) && 4208 (((*ViewSize)+ViewOffset) > Section->SizeOfSection.QuadPart) && 4209 (!Section->u.Flags.Reserve)) 4210 { 4211 /* Dubious */ 4212 (*ViewSize) = MIN(Section->SizeOfSection.QuadPart - ViewOffset, SIZE_T_MAX - PAGE_SIZE); 4213 } 4214 4215 *ViewSize = PAGE_ROUND_UP(*ViewSize); 4216 4217 MmLockSectionSegment(Segment); 4218 Status = MmMapViewOfSegment(AddressSpace, 4219 FALSE, 4220 Segment, 4221 BaseAddress, 4222 *ViewSize, 4223 Protect, 4224 ViewOffset, 4225 AllocationType & (MEM_TOP_DOWN|SEC_NO_CHANGE)); 4226 MmUnlockSectionSegment(Segment); 4227 if (!NT_SUCCESS(Status)) 4228 { 4229 goto Exit; 4230 } 4231 } 4232 4233 if (NotAtBase) 4234 Status = STATUS_IMAGE_NOT_AT_BASE; 4235 else 4236 Status = STATUS_SUCCESS; 4237 4238 Exit: 4239 4240 MmUnlockAddressSpace(AddressSpace); 4241 4242 if (IsAttached) 4243 { 4244 KeUnstackDetachProcess(&ApcState); 4245 } 4246 4247 return Status; 4248 } 4249 4250 /* 4251 * @unimplemented 4252 */ 4253 BOOLEAN 4254 NTAPI 4255 MmCanFileBeTruncated( 4256 _In_ PSECTION_OBJECT_POINTERS SectionObjectPointer, 4257 _In_opt_ PLARGE_INTEGER NewFileSize) 4258 { 4259 BOOLEAN Ret; 4260 PMM_SECTION_SEGMENT Segment; 4261 4262 /* Check whether an ImageSectionObject exists */ 4263 if (SectionObjectPointer->ImageSectionObject != NULL) 4264 { 4265 DPRINT1("ERROR: File can't be truncated because it has an image section\n"); 4266 return FALSE; 4267 } 4268 4269 Segment = MiGrabDataSection(SectionObjectPointer); 4270 if (!Segment) 4271 { 4272 /* There is no data section. It's fine to do anything. */ 4273 return TRUE; 4274 } 4275 4276 MmLockSectionSegment(Segment); 4277 if ((Segment->SectionCount == 0) || 4278 ((Segment->SectionCount == 1) && (SectionObjectPointer->SharedCacheMap != NULL))) 4279 { 4280 /* If the cache is the only one holding a reference to the segment, then it's fine to resize */ 4281 Ret = TRUE; 4282 } 4283 else if (NewFileSize != NULL) 4284 { 4285 /* We can't shrink, but we can extend */ 4286 Ret = NewFileSize->QuadPart >= Segment->RawLength.QuadPart; 4287 #if DBG 4288 if (!Ret) 4289 { 4290 DPRINT1("Cannot truncate data: New Size %I64d, Segment Size %I64d\n", NewFileSize->QuadPart, Segment->RawLength.QuadPart); 4291 } 4292 #endif 4293 } 4294 else 4295 { 4296 DPRINT1("ERROR: File can't be truncated because it has references held to its data section\n"); 4297 Ret = FALSE; 4298 } 4299 4300 MmUnlockSectionSegment(Segment); 4301 MmDereferenceSegment(Segment); 4302 4303 DPRINT("FIXME: didn't check for outstanding write probes\n"); 4304 4305 return Ret; 4306 } 4307 4308 static 4309 BOOLEAN 4310 MiPurgeImageSegment(PMM_SECTION_SEGMENT Segment) 4311 { 4312 PCACHE_SECTION_PAGE_TABLE PageTable; 4313 4314 MmLockSectionSegment(Segment); 4315 4316 /* Loop over all entries */ 4317 for (PageTable = RtlEnumerateGenericTable(&Segment->PageTable, TRUE); 4318 PageTable != NULL; 4319 PageTable = RtlEnumerateGenericTable(&Segment->PageTable, FALSE)) 4320 { 4321 for (ULONG i = 0; i < _countof(PageTable->PageEntries); i++) 4322 { 4323 ULONG_PTR Entry = PageTable->PageEntries[i]; 4324 LARGE_INTEGER Offset; 4325 4326 if (!Entry) 4327 continue; 4328 4329 if (IS_SWAP_FROM_SSE(Entry) || (SHARE_COUNT_FROM_SSE(Entry) > 0)) 4330 { 4331 /* I/O ongoing or swap entry. Someone mapped this file as we were not looking */ 4332 MmUnlockSectionSegment(Segment); 4333 return FALSE; 4334 } 4335 4336 /* Regular entry */ 4337 ASSERT(!IS_WRITE_SSE(Entry)); 4338 ASSERT(MmGetSavedSwapEntryPage(PFN_FROM_SSE(Entry)) == 0); 4339 4340 /* Properly remove using the used API */ 4341 Offset.QuadPart = PageTable->FileOffset.QuadPart + (i << PAGE_SHIFT); 4342 MmSetPageEntrySectionSegment(Segment, &Offset, 0); 4343 MmReleasePageMemoryConsumer(MC_USER, PFN_FROM_SSE(Entry)); 4344 } 4345 } 4346 4347 MmUnlockSectionSegment(Segment); 4348 4349 return TRUE; 4350 } 4351 4352 /* 4353 * @implemented 4354 */ 4355 BOOLEAN NTAPI 4356 MmFlushImageSection (IN PSECTION_OBJECT_POINTERS SectionObjectPointer, 4357 IN MMFLUSH_TYPE FlushType) 4358 { 4359 switch(FlushType) 4360 { 4361 case MmFlushForDelete: 4362 { 4363 /* 4364 * FIXME: Check for outstanding write probes on Data section. 4365 * How do we do that ? 4366 */ 4367 } 4368 /* Fall-through */ 4369 case MmFlushForWrite: 4370 { 4371 KIRQL OldIrql = MiAcquirePfnLock(); 4372 PMM_IMAGE_SECTION_OBJECT ImageSectionObject = SectionObjectPointer->ImageSectionObject; 4373 4374 DPRINT("Deleting or modifying %p\n", SectionObjectPointer); 4375 4376 /* Wait for concurrent creation or deletion of image to be done */ 4377 ImageSectionObject = SectionObjectPointer->ImageSectionObject; 4378 while (ImageSectionObject && (ImageSectionObject->SegFlags & (MM_SEGMENT_INCREATE | MM_SEGMENT_INDELETE))) 4379 { 4380 MiReleasePfnLock(OldIrql); 4381 KeDelayExecutionThread(KernelMode, FALSE, &TinyTime); 4382 OldIrql = MiAcquirePfnLock(); 4383 ImageSectionObject = SectionObjectPointer->ImageSectionObject; 4384 } 4385 4386 if (!ImageSectionObject) 4387 { 4388 DPRINT("No image section object. Accepting\n"); 4389 /* Nothing to do */ 4390 MiReleasePfnLock(OldIrql); 4391 return TRUE; 4392 } 4393 4394 /* Do we have open sections or mappings on it ? */ 4395 if ((ImageSectionObject->SectionCount) || (ImageSectionObject->MapCount)) 4396 { 4397 /* We do. No way to delete it */ 4398 MiReleasePfnLock(OldIrql); 4399 DPRINT("Denying. There are mappings open\n"); 4400 return FALSE; 4401 } 4402 4403 /* There are no sections open on it, but we must still have pages around. Discard everything */ 4404 ImageSectionObject->SegFlags |= MM_IMAGE_SECTION_FLUSH_DELETE; 4405 InterlockedIncrement64(&ImageSectionObject->RefCount); 4406 MiReleasePfnLock(OldIrql); 4407 4408 DPRINT("Purging\n"); 4409 4410 for (ULONG i = 0; i < ImageSectionObject->NrSegments; i++) 4411 { 4412 if (!MiPurgeImageSegment(&ImageSectionObject->Segments[i])) 4413 break; 4414 } 4415 4416 /* Grab lock again */ 4417 OldIrql = MiAcquirePfnLock(); 4418 4419 if (!(ImageSectionObject->SegFlags & MM_IMAGE_SECTION_FLUSH_DELETE)) 4420 { 4421 /* 4422 * Someone actually created a section while we were not looking. 4423 * Drop our ref and deny. 4424 * MmDereferenceSegmentWithLock releases Pfn lock 4425 */ 4426 MmDereferenceSegmentWithLock(&ImageSectionObject->Segments[0], OldIrql); 4427 return FALSE; 4428 } 4429 4430 /* We should be the last one holding a ref here. */ 4431 ASSERT(ImageSectionObject->RefCount == 1); 4432 ASSERT(ImageSectionObject->SectionCount == 0); 4433 4434 /* Dereference the first segment, this will free everything & release the lock */ 4435 MmDereferenceSegmentWithLock(&ImageSectionObject->Segments[0], OldIrql); 4436 return TRUE; 4437 } 4438 } 4439 return FALSE; 4440 } 4441 4442 /* 4443 * @implemented 4444 */ 4445 NTSTATUS 4446 NTAPI 4447 MmMapViewInSystemSpace (IN PVOID SectionObject, 4448 OUT PVOID * MappedBase, 4449 IN OUT PSIZE_T ViewSize) 4450 { 4451 LARGE_INTEGER SectionOffset; 4452 4453 SectionOffset.QuadPart = 0; 4454 4455 return MmMapViewInSystemSpaceEx(SectionObject, MappedBase, ViewSize, &SectionOffset, 0); 4456 } 4457 4458 NTSTATUS 4459 NTAPI 4460 MmMapViewInSystemSpaceEx ( 4461 _In_ PVOID SectionObject, 4462 _Outptr_result_bytebuffer_ (*ViewSize) PVOID *MappedBase, 4463 _Inout_ PSIZE_T ViewSize, 4464 _Inout_ PLARGE_INTEGER SectionOffset, 4465 _In_ ULONG_PTR Flags 4466 ) 4467 { 4468 PSECTION Section = SectionObject; 4469 PMM_SECTION_SEGMENT Segment; 4470 PMMSUPPORT AddressSpace; 4471 NTSTATUS Status; 4472 4473 UNREFERENCED_PARAMETER(Flags); 4474 4475 PAGED_CODE(); 4476 4477 if (MiIsRosSectionObject(SectionObject) == FALSE) 4478 { 4479 return MiMapViewInSystemSpace(SectionObject, 4480 &MmSession, 4481 MappedBase, 4482 ViewSize, 4483 SectionOffset); 4484 } 4485 4486 DPRINT("MmMapViewInSystemSpaceEx() called\n"); 4487 4488 /* unsupported for now */ 4489 ASSERT(Section->u.Flags.Image == 0); 4490 4491 Section = SectionObject; 4492 Segment = (PMM_SECTION_SEGMENT)Section->Segment; 4493 4494 if (*ViewSize == 0) 4495 { 4496 LONGLONG MapSizeLL; 4497 4498 /* Page-align the mapping */ 4499 SectionOffset->LowPart = PAGE_ROUND_DOWN(SectionOffset->LowPart); 4500 4501 if (!NT_SUCCESS(RtlLongLongSub(Section->SizeOfSection.QuadPart, SectionOffset->QuadPart, &MapSizeLL))) 4502 return STATUS_INVALID_VIEW_SIZE; 4503 4504 if (!NT_SUCCESS(RtlLongLongToSIZET(MapSizeLL, ViewSize))) 4505 return STATUS_INVALID_VIEW_SIZE; 4506 } 4507 else 4508 { 4509 LONGLONG HelperLL; 4510 4511 /* Get the map end */ 4512 if (!NT_SUCCESS(RtlLongLongAdd(SectionOffset->QuadPart, *ViewSize, &HelperLL))) 4513 return STATUS_INVALID_VIEW_SIZE; 4514 4515 /* Round it up, if needed */ 4516 if (HelperLL % PAGE_SIZE) 4517 { 4518 if (!NT_SUCCESS(RtlLongLongAdd(HelperLL, PAGE_SIZE - (HelperLL % PAGE_SIZE), &HelperLL))) 4519 return STATUS_INVALID_VIEW_SIZE; 4520 } 4521 4522 /* Now that we have the mapping end, we can align down its start */ 4523 SectionOffset->LowPart = PAGE_ROUND_DOWN(SectionOffset->LowPart); 4524 4525 /* Get the new size */ 4526 if (!NT_SUCCESS(RtlLongLongSub(HelperLL, SectionOffset->QuadPart, &HelperLL))) 4527 return STATUS_INVALID_VIEW_SIZE; 4528 4529 if (!NT_SUCCESS(RtlLongLongToSIZET(HelperLL, ViewSize))) 4530 return STATUS_INVALID_VIEW_SIZE; 4531 } 4532 4533 AddressSpace = MmGetKernelAddressSpace(); 4534 4535 MmLockAddressSpace(AddressSpace); 4536 4537 MmLockSectionSegment(Segment); 4538 4539 Status = MmMapViewOfSegment(AddressSpace, 4540 Section->u.Flags.Image, 4541 Segment, 4542 MappedBase, 4543 *ViewSize, 4544 PAGE_READWRITE, 4545 SectionOffset->QuadPart, 4546 SEC_RESERVE); 4547 4548 MmUnlockSectionSegment(Segment); 4549 MmUnlockAddressSpace(AddressSpace); 4550 4551 return Status; 4552 } 4553 4554 /* This function must be called with address space lock held */ 4555 NTSTATUS 4556 NTAPI 4557 MiRosUnmapViewInSystemSpace(IN PVOID MappedBase) 4558 { 4559 DPRINT("MmUnmapViewInSystemSpace() called\n"); 4560 4561 return MmUnmapViewOfSegment(MmGetKernelAddressSpace(), MappedBase); 4562 } 4563 4564 /********************************************************************** 4565 * NAME EXPORTED 4566 * MmCreateSection@ 4567 * 4568 * DESCRIPTION 4569 * Creates a section object. 4570 * 4571 * ARGUMENTS 4572 * SectionObject (OUT) 4573 * Caller supplied storage for the resulting pointer 4574 * to a SECTION_OBJECT instance; 4575 * 4576 * DesiredAccess 4577 * Specifies the desired access to the section can be a 4578 * combination of: 4579 * STANDARD_RIGHTS_REQUIRED | 4580 * SECTION_QUERY | 4581 * SECTION_MAP_WRITE | 4582 * SECTION_MAP_READ | 4583 * SECTION_MAP_EXECUTE 4584 * 4585 * ObjectAttributes [OPTIONAL] 4586 * Initialized attributes for the object can be used 4587 * to create a named section; 4588 * 4589 * MaximumSize 4590 * Maximizes the size of the memory section. Must be 4591 * non-NULL for a page-file backed section. 4592 * If value specified for a mapped file and the file is 4593 * not large enough, file will be extended. 4594 * 4595 * SectionPageProtection 4596 * Can be a combination of: 4597 * PAGE_READONLY | 4598 * PAGE_READWRITE | 4599 * PAGE_WRITEONLY | 4600 * PAGE_WRITECOPY 4601 * 4602 * AllocationAttributes 4603 * Can be a combination of: 4604 * SEC_IMAGE | 4605 * SEC_RESERVE 4606 * 4607 * FileHandle 4608 * Handle to a file to create a section mapped to a file 4609 * instead of a memory backed section; 4610 * 4611 * File 4612 * Unknown. 4613 * 4614 * RETURN VALUE 4615 * Status. 4616 * 4617 * @implemented 4618 */ 4619 NTSTATUS NTAPI 4620 MmCreateSection (OUT PVOID * Section, 4621 IN ACCESS_MASK DesiredAccess, 4622 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, 4623 IN PLARGE_INTEGER MaximumSize, 4624 IN ULONG SectionPageProtection, 4625 IN ULONG AllocationAttributes, 4626 IN HANDLE FileHandle OPTIONAL, 4627 IN PFILE_OBJECT FileObject OPTIONAL) 4628 { 4629 NTSTATUS Status; 4630 ULONG Protection; 4631 PSECTION *SectionObject = (PSECTION *)Section; 4632 BOOLEAN FileLock = FALSE; 4633 4634 /* Check if an ARM3 section is being created instead */ 4635 if (!(AllocationAttributes & (SEC_IMAGE | SEC_PHYSICALMEMORY))) 4636 { 4637 if (!(FileObject) && !(FileHandle)) 4638 { 4639 return MmCreateArm3Section(Section, 4640 DesiredAccess, 4641 ObjectAttributes, 4642 MaximumSize, 4643 SectionPageProtection, 4644 AllocationAttributes &~ 1, 4645 FileHandle, 4646 FileObject); 4647 } 4648 } 4649 4650 /* Convert section flag to page flag */ 4651 if (AllocationAttributes & SEC_NOCACHE) SectionPageProtection |= PAGE_NOCACHE; 4652 4653 /* Check to make sure the protection is correct. Nt* does this already */ 4654 Protection = MiMakeProtectionMask(SectionPageProtection); 4655 if (Protection == MM_INVALID_PROTECTION) 4656 { 4657 DPRINT1("Page protection is invalid\n"); 4658 return STATUS_INVALID_PAGE_PROTECTION; 4659 } 4660 4661 /* Check if this is going to be a data or image backed file section */ 4662 if ((FileHandle) || (FileObject)) 4663 { 4664 /* These cannot be mapped with large pages */ 4665 if (AllocationAttributes & SEC_LARGE_PAGES) 4666 { 4667 DPRINT1("Large pages cannot be used with an image mapping\n"); 4668 return STATUS_INVALID_PARAMETER_6; 4669 } 4670 4671 /* Did the caller pass a file object ? */ 4672 if (FileObject) 4673 { 4674 /* Reference the object directly */ 4675 ObReferenceObject(FileObject); 4676 4677 /* We don't create image mappings with file objects */ 4678 AllocationAttributes &= ~SEC_IMAGE; 4679 } 4680 else 4681 { 4682 /* Reference the file handle to get the object */ 4683 Status = ObReferenceObjectByHandle(FileHandle, 4684 MmMakeFileAccess[Protection], 4685 IoFileObjectType, 4686 ExGetPreviousMode(), 4687 (PVOID*)&FileObject, 4688 NULL); 4689 if (!NT_SUCCESS(Status)) 4690 { 4691 DPRINT1("Failed to get a handle to the FO: %lx\n", Status); 4692 return Status; 4693 } 4694 4695 /* Lock the file */ 4696 Status = FsRtlAcquireToCreateMappedSection(FileObject, SectionPageProtection); 4697 if (!NT_SUCCESS(Status)) 4698 { 4699 ObDereferenceObject(FileObject); 4700 return Status; 4701 } 4702 4703 FileLock = TRUE; 4704 4705 /* Deny access if there are writes on the file */ 4706 #if 0 4707 if ((AllocationAttributes & SEC_IMAGE) && (Status == STATUS_FILE_LOCKED_WITH_WRITERS)) 4708 { 4709 DPRINT1("Cannot create image maps with writers open on the file!\n"); 4710 Status = STATUS_ACCESS_DENIED; 4711 goto Quit; 4712 } 4713 #else 4714 if ((AllocationAttributes & SEC_IMAGE) && (Status == STATUS_FILE_LOCKED_WITH_WRITERS)) 4715 DPRINT1("Creating image map with writers open on the file!\n"); 4716 #endif 4717 } 4718 } 4719 else 4720 { 4721 /* A handle must be supplied with SEC_IMAGE, as this is the no-handle path */ 4722 if (AllocationAttributes & SEC_IMAGE) return STATUS_INVALID_FILE_FOR_SECTION; 4723 } 4724 4725 if (AllocationAttributes & SEC_IMAGE) 4726 { 4727 Status = MmCreateImageSection(SectionObject, 4728 DesiredAccess, 4729 ObjectAttributes, 4730 MaximumSize, 4731 SectionPageProtection, 4732 AllocationAttributes, 4733 FileObject); 4734 } 4735 #ifndef NEWCC 4736 else if (FileObject != NULL) 4737 { 4738 Status = MmCreateDataFileSection(SectionObject, 4739 DesiredAccess, 4740 ObjectAttributes, 4741 MaximumSize, 4742 SectionPageProtection, 4743 AllocationAttributes, 4744 FileObject, 4745 FileHandle != NULL); 4746 } 4747 #else 4748 else if (FileHandle != NULL || FileObject != NULL) 4749 { 4750 Status = MmCreateCacheSection(SectionObject, 4751 DesiredAccess, 4752 ObjectAttributes, 4753 MaximumSize, 4754 SectionPageProtection, 4755 AllocationAttributes, 4756 FileObject); 4757 } 4758 #endif 4759 else 4760 { 4761 /* All cases should be handled above */ 4762 Status = STATUS_INVALID_PARAMETER; 4763 } 4764 4765 if (FileLock) 4766 FsRtlReleaseFile(FileObject); 4767 if (FileObject) 4768 ObDereferenceObject(FileObject); 4769 4770 return Status; 4771 } 4772 4773 BOOLEAN 4774 NTAPI 4775 MmArePagesResident( 4776 _In_ PEPROCESS Process, 4777 _In_ PVOID Address, 4778 _In_ ULONG Length) 4779 { 4780 PMEMORY_AREA MemoryArea; 4781 BOOLEAN Ret = TRUE; 4782 PMM_SECTION_SEGMENT Segment; 4783 LARGE_INTEGER SegmentOffset, RangeEnd; 4784 PMMSUPPORT AddressSpace = Process ? &Process->Vm : MmGetKernelAddressSpace(); 4785 4786 MmLockAddressSpace(AddressSpace); 4787 4788 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address); 4789 if (MemoryArea == NULL) 4790 { 4791 MmUnlockAddressSpace(AddressSpace); 4792 return FALSE; 4793 } 4794 4795 /* Only supported in old Mm for now */ 4796 ASSERT(MemoryArea->Type == MEMORY_AREA_SECTION_VIEW); 4797 /* For file mappings */ 4798 ASSERT(MemoryArea->VadNode.u.VadFlags.VadType != VadImageMap); 4799 4800 Segment = MemoryArea->SectionData.Segment; 4801 MmLockSectionSegment(Segment); 4802 4803 SegmentOffset.QuadPart = PAGE_ROUND_DOWN(Address) - MA_GetStartingAddress(MemoryArea) 4804 + MemoryArea->SectionData.ViewOffset; 4805 RangeEnd.QuadPart = PAGE_ROUND_UP((ULONG_PTR)Address + Length) - MA_GetStartingAddress(MemoryArea) 4806 + MemoryArea->SectionData.ViewOffset; 4807 4808 while (SegmentOffset.QuadPart < RangeEnd.QuadPart) 4809 { 4810 ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &SegmentOffset); 4811 if ((Entry == 0) || IS_SWAP_FROM_SSE(Entry)) 4812 { 4813 Ret = FALSE; 4814 break; 4815 } 4816 SegmentOffset.QuadPart += PAGE_SIZE; 4817 } 4818 4819 MmUnlockSectionSegment(Segment); 4820 4821 MmUnlockAddressSpace(AddressSpace); 4822 return Ret; 4823 } 4824 4825 /* Like CcPurgeCache but for the in-memory segment */ 4826 BOOLEAN 4827 NTAPI 4828 MmPurgeSegment( 4829 _In_ PSECTION_OBJECT_POINTERS SectionObjectPointer, 4830 _In_opt_ PLARGE_INTEGER Offset, 4831 _In_ ULONG Length) 4832 { 4833 LARGE_INTEGER PurgeStart, PurgeEnd; 4834 PMM_SECTION_SEGMENT Segment; 4835 4836 Segment = MiGrabDataSection(SectionObjectPointer); 4837 if (!Segment) 4838 { 4839 /* Nothing to purge */ 4840 return TRUE; 4841 } 4842 4843 PurgeStart.QuadPart = Offset ? Offset->QuadPart : 0LL; 4844 if (Length && Offset) 4845 { 4846 if (!NT_SUCCESS(RtlLongLongAdd(PurgeStart.QuadPart, Length, &PurgeEnd.QuadPart))) 4847 return FALSE; 4848 } 4849 4850 MmLockSectionSegment(Segment); 4851 4852 if (!Length || !Offset) 4853 { 4854 /* We must calculate the length for ourselves */ 4855 /* FIXME: All of this is suboptimal */ 4856 ULONG ElemCount = RtlNumberGenericTableElements(&Segment->PageTable); 4857 /* No page. Nothing to purge */ 4858 if (!ElemCount) 4859 { 4860 MmUnlockSectionSegment(Segment); 4861 MmDereferenceSegment(Segment); 4862 return TRUE; 4863 } 4864 4865 PCACHE_SECTION_PAGE_TABLE PageTable = RtlGetElementGenericTable(&Segment->PageTable, ElemCount - 1); 4866 PurgeEnd.QuadPart = PageTable->FileOffset.QuadPart + _countof(PageTable->PageEntries) * PAGE_SIZE; 4867 } 4868 4869 while (PurgeStart.QuadPart < PurgeEnd.QuadPart) 4870 { 4871 ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &PurgeStart); 4872 4873 if (Entry == 0) 4874 { 4875 PurgeStart.QuadPart += PAGE_SIZE; 4876 continue; 4877 } 4878 4879 if (IS_SWAP_FROM_SSE(Entry)) 4880 { 4881 ASSERT(SWAPENTRY_FROM_SSE(Entry) == MM_WAIT_ENTRY); 4882 /* The page is currently being read. Meaning someone will need it soon. Bad luck */ 4883 MmUnlockSectionSegment(Segment); 4884 MmDereferenceSegment(Segment); 4885 return FALSE; 4886 } 4887 4888 if (IS_WRITE_SSE(Entry)) 4889 { 4890 /* We're trying to purge an entry which is being written. Restart this loop iteration */ 4891 MmUnlockSectionSegment(Segment); 4892 KeDelayExecutionThread(KernelMode, FALSE, &TinyTime); 4893 MmLockSectionSegment(Segment); 4894 continue; 4895 } 4896 4897 if (SHARE_COUNT_FROM_SSE(Entry) > 0) 4898 { 4899 /* This page is currently in use. Bad luck */ 4900 MmUnlockSectionSegment(Segment); 4901 MmDereferenceSegment(Segment); 4902 return FALSE; 4903 } 4904 4905 /* We can let this page go */ 4906 MmSetPageEntrySectionSegment(Segment, &PurgeStart, 0); 4907 MmReleasePageMemoryConsumer(MC_USER, PFN_FROM_SSE(Entry)); 4908 4909 PurgeStart.QuadPart += PAGE_SIZE; 4910 } 4911 4912 /* This page is currently in use. Bad luck */ 4913 MmUnlockSectionSegment(Segment); 4914 MmDereferenceSegment(Segment); 4915 return TRUE; 4916 } 4917 4918 NTSTATUS 4919 NTAPI 4920 MmMakeDataSectionResident( 4921 _In_ PSECTION_OBJECT_POINTERS SectionObjectPointer, 4922 _In_ LONGLONG Offset, 4923 _In_ ULONG Length, 4924 _In_ PLARGE_INTEGER ValidDataLength) 4925 { 4926 PMM_SECTION_SEGMENT Segment = MiGrabDataSection(SectionObjectPointer); 4927 4928 /* There must be a segment for this call */ 4929 ASSERT(Segment); 4930 4931 NTSTATUS Status = MmMakeSegmentResident(Segment, Offset, Length, ValidDataLength, FALSE); 4932 4933 MmDereferenceSegment(Segment); 4934 4935 return Status; 4936 } 4937 4938 NTSTATUS 4939 NTAPI 4940 MmFlushSegment( 4941 _In_ PSECTION_OBJECT_POINTERS SectionObjectPointer, 4942 _In_opt_ PLARGE_INTEGER Offset, 4943 _In_ ULONG Length, 4944 _Out_opt_ PIO_STATUS_BLOCK Iosb) 4945 { 4946 LARGE_INTEGER FlushStart, FlushEnd; 4947 NTSTATUS Status; 4948 4949 if (Offset) 4950 { 4951 FlushStart = *Offset; 4952 Status = RtlLongLongAdd(FlushStart.QuadPart, Length, &FlushEnd.QuadPart); 4953 if (!NT_SUCCESS(Status)) 4954 return Status; 4955 } 4956 4957 if (Iosb) 4958 Iosb->Information = 0; 4959 4960 PMM_SECTION_SEGMENT Segment = MiGrabDataSection(SectionObjectPointer); 4961 if (!Segment) 4962 { 4963 /* Nothing to flush */ 4964 if (Iosb) 4965 Iosb->Status = STATUS_SUCCESS; 4966 return STATUS_SUCCESS; 4967 } 4968 4969 ASSERT(*Segment->Flags & MM_DATAFILE_SEGMENT); 4970 4971 MmLockSectionSegment(Segment); 4972 4973 if (!Offset) 4974 { 4975 FlushStart.QuadPart = 0; 4976 4977 /* FIXME: All of this is suboptimal */ 4978 ULONG ElemCount = RtlNumberGenericTableElements(&Segment->PageTable); 4979 /* No page. Nothing to flush */ 4980 if (!ElemCount) 4981 { 4982 MmUnlockSectionSegment(Segment); 4983 MmDereferenceSegment(Segment); 4984 if (Iosb) 4985 { 4986 Iosb->Status = STATUS_SUCCESS; 4987 Iosb->Information = 0; 4988 } 4989 return STATUS_SUCCESS; 4990 } 4991 4992 PCACHE_SECTION_PAGE_TABLE PageTable = RtlGetElementGenericTable(&Segment->PageTable, ElemCount - 1); 4993 FlushEnd.QuadPart = PageTable->FileOffset.QuadPart + _countof(PageTable->PageEntries) * PAGE_SIZE; 4994 } 4995 4996 FlushStart.QuadPart >>= PAGE_SHIFT; 4997 FlushStart.QuadPart <<= PAGE_SHIFT; 4998 4999 while (FlushStart.QuadPart < FlushEnd.QuadPart) 5000 { 5001 ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &FlushStart); 5002 5003 if (IS_DIRTY_SSE(Entry)) 5004 { 5005 MmCheckDirtySegment(Segment, &FlushStart, FALSE, FALSE); 5006 5007 if (Iosb) 5008 Iosb->Information += PAGE_SIZE; 5009 } 5010 5011 FlushStart.QuadPart += PAGE_SIZE; 5012 } 5013 5014 MmUnlockSectionSegment(Segment); 5015 MmDereferenceSegment(Segment); 5016 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