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