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