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