Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

6445 lines
202 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. creasect.c
  5. Abstract:
  6. This module contains the routines which implement the
  7. NtCreateSection and NtOpenSection.
  8. Author:
  9. Lou Perazzoli (loup) 22-May-1989
  10. Landy Wang (landyw) 02-Jun-1997
  11. Revision History:
  12. --*/
  13. #include "mi.h"
  14. const ULONG MMCONTROL = 'aCmM';
  15. const ULONG MMTEMPORARY = 'xxmM';
  16. #define MM_SIZE_OF_LARGEST_IMAGE ((ULONG)0x77000000)
  17. #define MM_MAXIMUM_IMAGE_HEADER (2 * PAGE_SIZE)
  18. extern SIZE_T MmAllocationFragment;
  19. //
  20. // The maximum number of image object (object table entries) is
  21. // the number which will fit into the MM_MAXIMUM_IMAGE_HEADER with
  22. // the start of the PE image header in the last word of the first.
  23. //
  24. #define MM_MAXIMUM_IMAGE_SECTIONS \
  25. ((MM_MAXIMUM_IMAGE_HEADER - (PAGE_SIZE + sizeof(IMAGE_NT_HEADERS))) / \
  26. sizeof(IMAGE_SECTION_HEADER))
  27. #if DBG
  28. ULONG MiMakeImageFloppy[2];
  29. ULONG_PTR MiMatchSectionBase;
  30. #endif
  31. extern POBJECT_TYPE IoFileObjectType;
  32. CCHAR MmImageProtectionArray[16] = {
  33. MM_NOACCESS,
  34. MM_EXECUTE,
  35. MM_READONLY,
  36. MM_EXECUTE_READ,
  37. MM_WRITECOPY,
  38. MM_EXECUTE_WRITECOPY,
  39. MM_WRITECOPY,
  40. MM_EXECUTE_WRITECOPY,
  41. MM_NOACCESS,
  42. MM_EXECUTE,
  43. MM_READONLY,
  44. MM_EXECUTE_READ,
  45. MM_READWRITE,
  46. MM_EXECUTE_READWRITE,
  47. MM_READWRITE,
  48. MM_EXECUTE_READWRITE };
  49. VOID
  50. MiUpdateImageHeaderPage (
  51. IN PMMPTE PointerPte,
  52. IN PFN_NUMBER PageFrameNumber,
  53. IN PCONTROL_AREA ControlArea,
  54. IN LOGICAL MarkModified
  55. );
  56. CCHAR
  57. MiGetImageProtection (
  58. IN ULONG SectionCharacteristics
  59. );
  60. NTSTATUS
  61. MiVerifyImageHeader (
  62. IN PIMAGE_NT_HEADERS NtHeader,
  63. IN PIMAGE_DOS_HEADER DosHeader,
  64. IN ULONG NtHeaderSize
  65. );
  66. LOGICAL
  67. MiCheckDosCalls (
  68. IN PIMAGE_OS2_HEADER Os2Header,
  69. IN ULONG HeaderSize
  70. );
  71. PCONTROL_AREA
  72. MiFindImageSectionObject (
  73. IN PFILE_OBJECT File,
  74. IN PLOGICAL GlobalNeeded
  75. );
  76. VOID
  77. MiInsertImageSectionObject (
  78. IN PFILE_OBJECT File,
  79. IN PCONTROL_AREA ControlArea
  80. );
  81. LOGICAL
  82. MiFlushDataSection (
  83. IN PFILE_OBJECT File
  84. );
  85. PVOID
  86. MiCopyHeaderIfResident (
  87. IN PFILE_OBJECT File,
  88. IN PFN_NUMBER ImagePageFrameNumber
  89. );
  90. #ifdef ALLOC_PRAGMA
  91. #pragma alloc_text(PAGE,MiCreateImageFileMap)
  92. #pragma alloc_text(PAGE,NtCreateSection)
  93. #pragma alloc_text(PAGE,NtOpenSection)
  94. #pragma alloc_text(PAGE,MiGetImageProtection)
  95. #pragma alloc_text(PAGE,MiVerifyImageHeader)
  96. #pragma alloc_text(PAGE,MiCheckDosCalls)
  97. #pragma alloc_text(PAGE,MiCreatePagingFileMap)
  98. #pragma alloc_text(PAGE,MiCreateDataFileMap)
  99. #endif
  100. #pragma pack (1)
  101. typedef struct _PHARLAP_CONFIG {
  102. UCHAR uchCopyRight[0x32];
  103. USHORT usType;
  104. USHORT usRsv1;
  105. USHORT usRsv2;
  106. USHORT usSign;
  107. } CONFIGPHARLAP, *PCONFIGPHARLAP;
  108. #pragma pack ()
  109. NTSTATUS
  110. NtCreateSection (
  111. OUT PHANDLE SectionHandle,
  112. IN ACCESS_MASK DesiredAccess,
  113. IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
  114. IN PLARGE_INTEGER MaximumSize OPTIONAL,
  115. IN ULONG SectionPageProtection,
  116. IN ULONG AllocationAttributes,
  117. IN HANDLE FileHandle OPTIONAL
  118. )
  119. /*++
  120. Routine Description:
  121. This function creates a section object and opens a handle to the object
  122. with the specified desired access.
  123. Arguments:
  124. SectionHandle - A pointer to a variable that will
  125. receive the section object handle value.
  126. DesiredAccess - The desired types of access for the section.
  127. DesiredAccess Flags
  128. EXECUTE - Execute access to the section is
  129. desired.
  130. READ - Read access to the section is desired.
  131. WRITE - Write access to the section is desired.
  132. ObjectAttributes - Supplies a pointer to an object attributes structure.
  133. MaximumSize - Supplies the maximum size of the section in bytes.
  134. This value is rounded up to the host page size and
  135. specifies the size of the section (page file
  136. backed section) or the maximum size to which a
  137. file can be extended or mapped (file backed
  138. section).
  139. SectionPageProtection - Supplies the protection to place on each page
  140. in the section. One of PAGE_READ, PAGE_READWRITE, PAGE_EXECUTE,
  141. or PAGE_WRITECOPY and, optionally, PAGE_NOCACHE may be specified.
  142. AllocationAttributes - Supplies a set of flags that describe the
  143. allocation attributes of the section.
  144. AllocationAttributes Flags
  145. SEC_BASED - The section is a based section and will be
  146. allocated at the same virtual address in each process
  147. address space that receives the section. This does not
  148. imply that addresses are reserved for based sections.
  149. Rather if the section cannot be mapped at the based address
  150. an error is returned.
  151. SEC_RESERVE - All pages of the section are set to the
  152. reserved state.
  153. SEC_COMMIT - All pages of the section are set to the commit
  154. state.
  155. SEC_IMAGE - The file specified by the file handle is an
  156. executable image file.
  157. SEC_FILE - The file specified by the file handle is a mapped
  158. file. If a file handle is supplied and neither
  159. SEC_IMAGE or SEC_FILE is supplied, SEC_FILE is
  160. assumed.
  161. SEC_NO_CHANGE - Once the file is mapped, the protection cannot
  162. be changed nor can the view be unmapped. The
  163. view is unmapped when the process is deleted.
  164. Cannot be used with SEC_IMAGE.
  165. FileHandle - Supplies an optional handle of an open file object.
  166. If the value of this handle is null, then the
  167. section will be backed by a paging file. Otherwise
  168. the section is backed by the specified data file.
  169. Return Value:
  170. NTSTATUS.
  171. --*/
  172. {
  173. NTSTATUS Status;
  174. PVOID Section;
  175. HANDLE Handle;
  176. LARGE_INTEGER LargeSize;
  177. LARGE_INTEGER CapturedSize;
  178. ULONG RetryCount;
  179. PCONTROL_AREA ControlArea;
  180. if ((AllocationAttributes & ~(SEC_COMMIT | SEC_RESERVE | SEC_BASED |
  181. SEC_IMAGE | SEC_NOCACHE | SEC_NO_CHANGE)) != 0) {
  182. return STATUS_INVALID_PARAMETER_6;
  183. }
  184. if ((AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_IMAGE)) == 0) {
  185. return STATUS_INVALID_PARAMETER_6;
  186. }
  187. if ((AllocationAttributes & SEC_IMAGE) &&
  188. (AllocationAttributes & (SEC_COMMIT | SEC_RESERVE |
  189. SEC_NOCACHE | SEC_NO_CHANGE))) {
  190. return STATUS_INVALID_PARAMETER_6;
  191. }
  192. if ((AllocationAttributes & SEC_COMMIT) &&
  193. (AllocationAttributes & SEC_RESERVE)) {
  194. return STATUS_INVALID_PARAMETER_6;
  195. }
  196. //
  197. // Check the SectionProtection Flag.
  198. //
  199. if ((SectionPageProtection & PAGE_NOCACHE) ||
  200. (SectionPageProtection & PAGE_GUARD) ||
  201. (SectionPageProtection & PAGE_NOACCESS)) {
  202. //
  203. // No cache is only specified through SEC_NOCACHE option in the
  204. // allocation attributes.
  205. //
  206. return STATUS_INVALID_PAGE_PROTECTION;
  207. }
  208. if (KeGetPreviousMode() != KernelMode) {
  209. try {
  210. ProbeForWriteHandle(SectionHandle);
  211. if (ARGUMENT_PRESENT (MaximumSize)) {
  212. #if !defined (_WIN64)
  213. //
  214. // Note we only probe for byte alignment because prior to 2195,
  215. // we never probed at all! We don't want to break user apps
  216. // that had bad alignment if they worked before.
  217. //
  218. ProbeForReadSmallStructure(MaximumSize, sizeof(LARGE_INTEGER), sizeof(UCHAR));
  219. #else
  220. ProbeForReadSmallStructure(MaximumSize, sizeof(LARGE_INTEGER), sizeof(LARGE_INTEGER));
  221. #endif
  222. LargeSize = *MaximumSize;
  223. }
  224. else {
  225. ZERO_LARGE (LargeSize);
  226. }
  227. } except (EXCEPTION_EXECUTE_HANDLER) {
  228. return GetExceptionCode();
  229. }
  230. }
  231. else {
  232. if (ARGUMENT_PRESENT (MaximumSize)) {
  233. LargeSize = *MaximumSize;
  234. }
  235. else {
  236. ZERO_LARGE (LargeSize);
  237. }
  238. }
  239. RetryCount = 0;
  240. retry:
  241. CapturedSize = LargeSize;
  242. ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
  243. Status = MmCreateSection (&Section,
  244. DesiredAccess,
  245. ObjectAttributes,
  246. &CapturedSize,
  247. SectionPageProtection,
  248. AllocationAttributes,
  249. FileHandle,
  250. NULL);
  251. ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
  252. if (!NT_SUCCESS(Status)) {
  253. if ((Status == STATUS_FILE_LOCK_CONFLICT) &&
  254. (RetryCount < 3)) {
  255. //
  256. // The file system may have prevented this from working
  257. // due to log file flushing. Delay and try again.
  258. //
  259. RetryCount += 1;
  260. KeDelayExecutionThread (KernelMode,
  261. FALSE,
  262. (PLARGE_INTEGER)&MmHalfSecond);
  263. goto retry;
  264. }
  265. return Status;
  266. }
  267. ControlArea = ((PSECTION)Section)->Segment->ControlArea;
  268. #if DBG
  269. if (MmDebug & MM_DBG_SECTIONS) {
  270. DbgPrint ("inserting section %p control %p\n", Section, ControlArea);
  271. }
  272. #endif
  273. if ((ControlArea != NULL) && (ControlArea->FilePointer != NULL)) {
  274. CcZeroEndOfLastPage (ControlArea->FilePointer);
  275. }
  276. //
  277. // Note if the insertion fails, Ob will dereference the object for us.
  278. //
  279. Status = ObInsertObject (Section,
  280. NULL,
  281. DesiredAccess,
  282. 0,
  283. (PVOID *)NULL,
  284. &Handle);
  285. if (NT_SUCCESS(Status)) {
  286. try {
  287. *SectionHandle = Handle;
  288. } except (EXCEPTION_EXECUTE_HANDLER) {
  289. //
  290. // If the write attempt fails, then do not report an error.
  291. // When the caller attempts to access the handle value,
  292. // an access violation will occur.
  293. //
  294. }
  295. }
  296. return Status;
  297. }
  298. NTSTATUS
  299. MmCreateSection (
  300. OUT PVOID *SectionObject,
  301. IN ACCESS_MASK DesiredAccess,
  302. IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
  303. IN PLARGE_INTEGER InputMaximumSize,
  304. IN ULONG SectionPageProtection,
  305. IN ULONG AllocationAttributes,
  306. IN HANDLE FileHandle OPTIONAL,
  307. IN PFILE_OBJECT FileObject OPTIONAL
  308. )
  309. /*++
  310. Routine Description:
  311. This function creates a section object and opens a handle to the object
  312. with the specified desired access.
  313. Arguments:
  314. Section - A pointer to a variable that will
  315. receive the section object address.
  316. DesiredAccess - The desired types of access for the section.
  317. DesiredAccess Flags
  318. EXECUTE - Execute access to the section is desired.
  319. READ - Read access to the section is desired.
  320. WRITE - Write access to the section is desired.
  321. ObjectAttributes - Supplies a pointer to an object attributes structure.
  322. InputMaximumSize - Supplies the maximum size of the section in bytes.
  323. This value is rounded up to the host page size and
  324. specifies the size of the section (page file
  325. backed section) or the maximum size to which a
  326. file can be extended or mapped (file backed
  327. section).
  328. SectionPageProtection - Supplies the protection to place on each page
  329. in the section. One of PAGE_READ, PAGE_READWRITE,
  330. PAGE_EXECUTE, or PAGE_WRITECOPY and, optionally,
  331. PAGE_NOCACHE may be specified.
  332. AllocationAttributes - Supplies a set of flags that describe the
  333. allocation attributes of the section.
  334. AllocationAttributes Flags
  335. SEC_BASED - The section is a based section and will be
  336. allocated at the same virtual address in each process
  337. address space that receives the section. This does not
  338. imply that addresses are reserved for based sections.
  339. Rather if the section cannot be mapped at the based address
  340. an error is returned.
  341. SEC_RESERVE - All pages of the section are set to the
  342. reserved state.
  343. SEC_COMMIT - All pages of the section are set to the commit state.
  344. SEC_IMAGE - The file specified by the file handle is an
  345. executable image file.
  346. SEC_FILE - The file specified by the file handle is a mapped
  347. file. If a file handle is supplied and neither
  348. SEC_IMAGE or SEC_FILE is supplied, SEC_FILE is
  349. assumed.
  350. FileHandle - Supplies an optional handle of an open file object.
  351. If the value of this handle is null, then the
  352. section will be backed by a paging file. Otherwise
  353. the section is backed by the specified data file.
  354. FileObject - Supplies an optional pointer to the file object. If this
  355. value is NULL and the FileHandle is NULL, then there is
  356. no file to map (image or mapped file). If this value
  357. is specified, then the File is to be mapped as a MAPPED FILE
  358. and NO file size checking will be performed.
  359. ONLY THE SYSTEM CACHE SHOULD PROVIDE A FILE OBJECT WITH THE
  360. CALL!! as this is optimized to not check the size, only do
  361. data mapping, no protection check, etc.
  362. Note - Only one of FileHandle or File should be specified!
  363. Return Value:
  364. Returns the relevant NTSTATUS code.
  365. --*/
  366. {
  367. SECTION Section;
  368. PSECTION NewSection;
  369. PSUBSECTION Subsection;
  370. ULONG SubsectionSize;
  371. PSEGMENT Segment;
  372. PSEGMENT NewSegment;
  373. KPROCESSOR_MODE PreviousMode;
  374. KIRQL OldIrql;
  375. NTSTATUS Status;
  376. NTSTATUS Status2;
  377. PCONTROL_AREA ControlArea;
  378. PCONTROL_AREA NewControlArea;
  379. PCONTROL_AREA SegmentControlArea;
  380. ACCESS_MASK FileDesiredAccess;
  381. PFILE_OBJECT File;
  382. PEVENT_COUNTER Event;
  383. ULONG IgnoreFileSizing;
  384. ULONG ProtectionMask;
  385. ULONG ProtectMaskForAccess;
  386. ULONG FileAcquired;
  387. PEVENT_COUNTER SegmentEvent;
  388. LOGICAL FileSizeChecked;
  389. LARGE_INTEGER TempSectionSize;
  390. UINT64 EndOfFile;
  391. ULONG IncrementedRefCount;
  392. SIZE_T ControlAreaSize;
  393. PUINT64 MaximumSize;
  394. PMM_AVL_TABLE SectionBasedRoot;
  395. LOGICAL GlobalNeeded;
  396. PFILE_OBJECT ChangeFileReference;
  397. SIZE_T SizeOfSection;
  398. ULONG PagedPoolCharge;
  399. ULONG NonPagedPoolCharge;
  400. #if DBG
  401. PVOID PreviousSectionPointer;
  402. PreviousSectionPointer = (PVOID)-1;
  403. #endif
  404. NewControlArea = (PCONTROL_AREA)-1;
  405. UNREFERENCED_PARAMETER (DesiredAccess);
  406. IgnoreFileSizing = FALSE;
  407. FileAcquired = FALSE;
  408. FileSizeChecked = FALSE;
  409. IncrementedRefCount = FALSE;
  410. MaximumSize = (PUINT64) InputMaximumSize;
  411. //
  412. // Check allocation attributes flags.
  413. //
  414. File = (PFILE_OBJECT)NULL;
  415. ASSERT ((AllocationAttributes & ~(SEC_COMMIT | SEC_RESERVE | SEC_BASED |
  416. SEC_IMAGE | SEC_NOCACHE | SEC_NO_CHANGE)) == 0);
  417. ASSERT ((AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_IMAGE)) != 0);
  418. ASSERT (!((AllocationAttributes & SEC_IMAGE) &&
  419. (AllocationAttributes & (SEC_COMMIT | SEC_RESERVE |
  420. SEC_NOCACHE | SEC_NO_CHANGE))));
  421. ASSERT (!((AllocationAttributes & SEC_COMMIT) &&
  422. (AllocationAttributes & SEC_RESERVE)));
  423. ASSERT (!((SectionPageProtection & PAGE_NOCACHE) ||
  424. (SectionPageProtection & PAGE_GUARD) ||
  425. (SectionPageProtection & PAGE_NOACCESS)));
  426. if (AllocationAttributes & SEC_NOCACHE) {
  427. SectionPageProtection |= PAGE_NOCACHE;
  428. }
  429. //
  430. // Check the protection field.
  431. //
  432. ProtectionMask = MiMakeProtectionMask (SectionPageProtection);
  433. if (ProtectionMask == MM_INVALID_PROTECTION) {
  434. return STATUS_INVALID_PAGE_PROTECTION;
  435. }
  436. ProtectMaskForAccess = ProtectionMask & 0x7;
  437. FileDesiredAccess = MmMakeFileAccess[ProtectMaskForAccess];
  438. //
  439. // Get previous processor mode and probe output arguments if necessary.
  440. //
  441. PreviousMode = KeGetPreviousMode();
  442. Section.InitialPageProtection = SectionPageProtection;
  443. Section.Segment = (PSEGMENT)NULL;
  444. //
  445. // Initializing Segment is not needed for correctness, but
  446. // without it the compiler cannot compile this code W4 to check
  447. // for use of uninitialized variables.
  448. //
  449. Segment = (PSEGMENT)-1;
  450. if (ARGUMENT_PRESENT(FileHandle) || ARGUMENT_PRESENT(FileObject)) {
  451. //
  452. // Only one of FileHandle or FileObject should be supplied.
  453. // If a FileObject is supplied, this must be from the
  454. // file system and therefore the file's size should not
  455. // be checked.
  456. //
  457. if (ARGUMENT_PRESENT(FileObject)) {
  458. IgnoreFileSizing = TRUE;
  459. File = FileObject;
  460. //
  461. // Quick check to see if a control area already exists.
  462. //
  463. if (File->SectionObjectPointer->DataSectionObject) {
  464. ChangeFileReference = NULL;
  465. LOCK_PFN (OldIrql);
  466. ControlArea =
  467. (PCONTROL_AREA)(File->SectionObjectPointer->DataSectionObject);
  468. if ((ControlArea != NULL) &&
  469. (!ControlArea->u.Flags.BeingDeleted) &&
  470. (!ControlArea->u.Flags.BeingCreated)) {
  471. //
  472. // Control area exists and is not being deleted,
  473. // reference it.
  474. //
  475. NewSegment = ControlArea->Segment;
  476. if ((ControlArea->NumberOfSectionReferences == 0) &&
  477. (ControlArea->NumberOfMappedViews == 0) &&
  478. (ControlArea->ModifiedWriteCount == 0)) {
  479. //
  480. // Dereference the current file object (after releasing
  481. // the PFN lock) and reference this one.
  482. //
  483. ASSERT (ControlArea->FilePointer != NULL);
  484. ChangeFileReference = ControlArea->FilePointer;
  485. ControlArea->FilePointer = FileObject;
  486. }
  487. ControlArea->u.Flags.Accessed = 1;
  488. ControlArea->NumberOfSectionReferences += 1;
  489. if (ControlArea->DereferenceList.Flink != NULL) {
  490. //
  491. // Remove this from the list of unused segments.
  492. //
  493. RemoveEntryList (&ControlArea->DereferenceList);
  494. MI_UNUSED_SEGMENTS_REMOVE_CHARGE (ControlArea);
  495. ControlArea->DereferenceList.Flink = NULL;
  496. ControlArea->DereferenceList.Blink = NULL;
  497. }
  498. UNLOCK_PFN (OldIrql);
  499. //
  500. // Inform the object manager to defer this deletion by
  501. // queueing it to another thread to eliminate deadlocks
  502. // with the redirector.
  503. //
  504. if (ChangeFileReference != NULL) {
  505. ObDereferenceObjectDeferDelete (ChangeFileReference);
  506. ObReferenceObject (FileObject);
  507. }
  508. IncrementedRefCount = TRUE;
  509. Section.SizeOfSection.QuadPart = (LONGLONG)*MaximumSize;
  510. goto ReferenceObject;
  511. }
  512. UNLOCK_PFN (OldIrql);
  513. }
  514. ObReferenceObject (FileObject);
  515. }
  516. else {
  517. Status = ObReferenceObjectByHandle (FileHandle,
  518. FileDesiredAccess,
  519. IoFileObjectType,
  520. PreviousMode,
  521. (PVOID *)&File,
  522. NULL);
  523. if (!NT_SUCCESS(Status)) {
  524. return Status;
  525. }
  526. //
  527. // If this file doesn't have a section object pointer,
  528. // return an error.
  529. //
  530. if (File->SectionObjectPointer == NULL) {
  531. ObDereferenceObject (File);
  532. return STATUS_INVALID_FILE_FOR_SECTION;
  533. }
  534. }
  535. //
  536. // Check to see if the specified file already has a section.
  537. // If not, indicate in the file object's pointer to an FCB that
  538. // a section is being built. This synchronizes segment creation
  539. // for the file.
  540. //
  541. if (AllocationAttributes & SEC_IMAGE) {
  542. //
  543. // This control area is always just a place holder - the real one
  544. // is allocated in MiCreateImageFileMap and will be allocated
  545. // with the correct size and this one freed in a short while.
  546. //
  547. // This place holder must always be allocated as a large control
  548. // area so that it can be chained for the per-session case.
  549. //
  550. ControlAreaSize = sizeof(LARGE_CONTROL_AREA) + sizeof(SUBSECTION);
  551. //
  552. // For image sections, make sure that Cc has released all references
  553. // to the section due to a previous data section mapping. This will
  554. // decrease the chance that the image will need to be backed by
  555. // the pagefile because writeable data sections remain on the file.
  556. //
  557. CcWaitForUninitializeCacheMap (File);
  558. }
  559. else {
  560. //
  561. // Data files are mapped with larger subsections than images or
  562. // pagefile-backed shared memory. Factor that in here.
  563. //
  564. ControlAreaSize = sizeof(CONTROL_AREA) + sizeof(MSUBSECTION);
  565. }
  566. NewControlArea = ExAllocatePoolWithTag (NonPagedPool,
  567. ControlAreaSize,
  568. MMCONTROL);
  569. if (NewControlArea == NULL) {
  570. ObDereferenceObject (File);
  571. return STATUS_INSUFFICIENT_RESOURCES;
  572. }
  573. RtlZeroMemory (NewControlArea, ControlAreaSize);
  574. NewSegment = NULL;
  575. //
  576. // We only need the file resource if this was a user request, i.e. not
  577. // a call from the cache manager or file system.
  578. //
  579. if (ARGUMENT_PRESENT(FileHandle)) {
  580. PIRP tempIrp = (PIRP)FSRTL_FSP_TOP_LEVEL_IRP;
  581. Status = FsRtlAcquireToCreateMappedSection (File, SectionPageProtection);
  582. if (!NT_SUCCESS(Status)) {
  583. ExFreePool (NewControlArea);
  584. ObDereferenceObject (File);
  585. return Status;
  586. }
  587. IoSetTopLevelIrp(tempIrp);
  588. FileAcquired = TRUE;
  589. }
  590. //
  591. // Initializing GlobalNeeded is not needed for correctness, but
  592. // without it the compiler cannot compile this code W4 to check
  593. // for use of uninitialized variables.
  594. //
  595. GlobalNeeded = FALSE;
  596. //
  597. // Allocate an event to wait on in case the segment is in the
  598. // process of being deleted. This event cannot be allocated
  599. // with the PFN database locked as pool expansion would deadlock.
  600. //
  601. ReallocateandcheckSegment:
  602. SegmentEvent = MiGetEventCounter ();
  603. if (SegmentEvent == NULL) {
  604. if (FileAcquired) {
  605. IoSetTopLevelIrp (NULL);
  606. FsRtlReleaseFile (File);
  607. }
  608. ExFreePool (NewControlArea);
  609. ObDereferenceObject (File);
  610. return STATUS_INSUFFICIENT_RESOURCES;
  611. }
  612. RecheckSegment:
  613. LOCK_PFN (OldIrql);
  614. if (AllocationAttributes & SEC_IMAGE) {
  615. ControlArea = MiFindImageSectionObject (File, &GlobalNeeded);
  616. }
  617. else {
  618. ControlArea =
  619. (PCONTROL_AREA)(File->SectionObjectPointer->DataSectionObject);
  620. }
  621. if (ControlArea != NULL) {
  622. //
  623. // A segment already exists for this file. Make sure that it
  624. // is not in the process of being deleted, or being created.
  625. //
  626. if ((ControlArea->u.Flags.BeingDeleted) ||
  627. (ControlArea->u.Flags.BeingCreated)) {
  628. //
  629. // The segment object is in the process of being deleted or
  630. // created.
  631. // Check to see if another thread is waiting for the deletion,
  632. // otherwise create an event object to wait upon.
  633. //
  634. if (ControlArea->WaitingForDeletion == NULL) {
  635. //
  636. // Initialize an event and put its address in the control area.
  637. //
  638. ControlArea->WaitingForDeletion = SegmentEvent;
  639. Event = SegmentEvent;
  640. SegmentEvent = NULL;
  641. }
  642. else {
  643. Event = ControlArea->WaitingForDeletion;
  644. //
  645. // No interlock is needed for the RefCount increment as
  646. // no thread can be decrementing it since it is still
  647. // pointed to by the control area.
  648. //
  649. Event->RefCount += 1;
  650. }
  651. //
  652. // Release the PFN lock, the file lock, and wait for the event.
  653. //
  654. UNLOCK_PFN (OldIrql);
  655. if (FileAcquired) {
  656. IoSetTopLevelIrp (NULL);
  657. FsRtlReleaseFile (File);
  658. }
  659. KeWaitForSingleObject (&Event->Event,
  660. WrVirtualMemory,
  661. KernelMode,
  662. FALSE,
  663. (PLARGE_INTEGER)NULL);
  664. //
  665. // Before this event can be set, the control area
  666. // WaitingForDeletion field must be cleared (and may be
  667. // reinitialized to something else), but cannot be reset
  668. // to our local event. This allows us to dereference the
  669. // event count lock free.
  670. //
  671. #if 0
  672. //
  673. // Note that the control area cannot be referenced at this
  674. // point because it may have been freed.
  675. //
  676. ASSERT (Event != ControlArea->WaitingForDeletion);
  677. #endif
  678. if (FileAcquired) {
  679. Status = FsRtlAcquireToCreateMappedSection (File, SectionPageProtection);
  680. if (NT_SUCCESS(Status)) {
  681. PIRP tempIrp = (PIRP)FSRTL_FSP_TOP_LEVEL_IRP;
  682. IoSetTopLevelIrp (tempIrp);
  683. }
  684. else {
  685. ExFreePool (NewControlArea);
  686. ObDereferenceObject (File);
  687. return Status;
  688. }
  689. }
  690. MiFreeEventCounter (Event);
  691. if (SegmentEvent == NULL) {
  692. //
  693. // The event was freed from pool, allocate another
  694. // event in case we have to synchronize one more time.
  695. //
  696. goto ReallocateandcheckSegment;
  697. }
  698. goto RecheckSegment;
  699. }
  700. //
  701. // There is already a segment for this file, have
  702. // this section refer to that segment.
  703. // No need to reference the file object any more.
  704. //
  705. if ((ControlArea->u.Flags.ImageMappedInSystemSpace) &&
  706. (AllocationAttributes & SEC_IMAGE) &&
  707. (KeGetPreviousMode () != KernelMode)) {
  708. UNLOCK_PFN (OldIrql);
  709. MiFreeEventCounter (SegmentEvent);
  710. if (FileAcquired) {
  711. IoSetTopLevelIrp (NULL);
  712. FsRtlReleaseFile (File);
  713. }
  714. ExFreePool (NewControlArea);
  715. ObDereferenceObject (File);
  716. return STATUS_CONFLICTING_ADDRESSES;
  717. }
  718. NewSegment = ControlArea->Segment;
  719. ControlArea->u.Flags.Accessed = 1;
  720. ControlArea->NumberOfSectionReferences += 1;
  721. if (ControlArea->DereferenceList.Flink != NULL) {
  722. //
  723. // Remove this from the list of unused segments.
  724. //
  725. RemoveEntryList (&ControlArea->DereferenceList);
  726. MI_UNUSED_SEGMENTS_REMOVE_CHARGE (ControlArea);
  727. ControlArea->DereferenceList.Flink = NULL;
  728. ControlArea->DereferenceList.Blink = NULL;
  729. }
  730. IncrementedRefCount = TRUE;
  731. //
  732. // If this reference was not from the cache manager
  733. // up the count of user references.
  734. //
  735. if (IgnoreFileSizing == FALSE) {
  736. ControlArea->NumberOfUserReferences += 1;
  737. }
  738. }
  739. else {
  740. //
  741. // There is no segment associated with this file object.
  742. // Set the file object to refer to the new control area.
  743. //
  744. ControlArea = NewControlArea;
  745. ControlArea->u.Flags.BeingCreated = 1;
  746. if (AllocationAttributes & SEC_IMAGE) {
  747. if (GlobalNeeded == TRUE) {
  748. ControlArea->u.Flags.GlobalOnlyPerSession = 1;
  749. }
  750. MiInsertImageSectionObject (File, ControlArea);
  751. }
  752. else {
  753. #if DBG
  754. PreviousSectionPointer = File->SectionObjectPointer;
  755. #endif
  756. File->SectionObjectPointer->DataSectionObject = (PVOID) ControlArea;
  757. }
  758. }
  759. UNLOCK_PFN (OldIrql);
  760. if (SegmentEvent != NULL) {
  761. MiFreeEventCounter (SegmentEvent);
  762. }
  763. if (NewSegment != NULL) {
  764. //
  765. // A segment already exists for this file object.
  766. // If we're creating an imagemap, flush the data section
  767. // if there is one.
  768. //
  769. if (AllocationAttributes & SEC_IMAGE) {
  770. MiFlushDataSection (File);
  771. }
  772. //
  773. // Deallocate the new control area as it won't be needed.
  774. // Dereference the file object later when we're done with it.
  775. //
  776. ExFreePool (NewControlArea);
  777. //
  778. // The section is in paged pool, this can't be set until
  779. // the PFN mutex has been released.
  780. //
  781. if ((!IgnoreFileSizing) && (ControlArea->u.Flags.Image == 0)) {
  782. //
  783. // The file size in the segment may not match the current
  784. // file size, query the file system and get the file
  785. // size.
  786. //
  787. Status = FsRtlGetFileSize (File, (PLARGE_INTEGER)&EndOfFile);
  788. if (!NT_SUCCESS (Status)) {
  789. if (FileAcquired) {
  790. IoSetTopLevelIrp(NULL);
  791. FsRtlReleaseFile (File);
  792. FileAcquired = FALSE;
  793. }
  794. ObDereferenceObject (File);
  795. goto UnrefAndReturn;
  796. }
  797. if (EndOfFile == 0 && *MaximumSize == 0) {
  798. //
  799. // Can't map a zero length without specifying the maximum
  800. // size as non-zero.
  801. //
  802. Status = STATUS_MAPPED_FILE_SIZE_ZERO;
  803. if (FileAcquired) {
  804. IoSetTopLevelIrp(NULL);
  805. FsRtlReleaseFile (File);
  806. FileAcquired = FALSE;
  807. }
  808. ObDereferenceObject (File);
  809. goto UnrefAndReturn;
  810. }
  811. }
  812. else {
  813. //
  814. // The size is okay in the segment.
  815. //
  816. EndOfFile = (UINT64) NewSegment->SizeOfSegment;
  817. }
  818. if (FileAcquired) {
  819. IoSetTopLevelIrp(NULL);
  820. FsRtlReleaseFile (File);
  821. FileAcquired = FALSE;
  822. }
  823. ObDereferenceObject (File);
  824. if (*MaximumSize == 0) {
  825. Section.SizeOfSection.QuadPart = (LONGLONG)EndOfFile;
  826. FileSizeChecked = TRUE;
  827. }
  828. else if (EndOfFile >= *MaximumSize) {
  829. //
  830. // EndOfFile is greater than the MaximumSize,
  831. // use the specified maximum size.
  832. //
  833. Section.SizeOfSection.QuadPart = (LONGLONG)*MaximumSize;
  834. FileSizeChecked = TRUE;
  835. }
  836. else {
  837. //
  838. // Need to extend the section, make sure the file was
  839. // opened for write access.
  840. //
  841. if (((SectionPageProtection & PAGE_READWRITE) |
  842. (SectionPageProtection & PAGE_EXECUTE_READWRITE)) == 0) {
  843. Status = STATUS_SECTION_TOO_BIG;
  844. goto UnrefAndReturn;
  845. }
  846. Section.SizeOfSection.QuadPart = (LONGLONG)*MaximumSize;
  847. }
  848. }
  849. else {
  850. //
  851. // The file does not have an associated segment, create a segment
  852. // object.
  853. //
  854. PERFINFO_SECTION_CREATE1(File);
  855. if (AllocationAttributes & SEC_IMAGE) {
  856. Status = MiCreateImageFileMap (File, &Segment);
  857. }
  858. else {
  859. Status = MiCreateDataFileMap (File,
  860. &Segment,
  861. MaximumSize,
  862. SectionPageProtection,
  863. AllocationAttributes,
  864. IgnoreFileSizing);
  865. ASSERT (PreviousSectionPointer == File->SectionObjectPointer);
  866. }
  867. if (!NT_SUCCESS(Status)) {
  868. //
  869. // Lock the PFN database and check to see if another thread has
  870. // tried to create a segment to the file object at the same
  871. // time.
  872. //
  873. LOCK_PFN (OldIrql);
  874. Event = ControlArea->WaitingForDeletion;
  875. ControlArea->WaitingForDeletion = NULL;
  876. ASSERT (ControlArea->u.Flags.FilePointerNull == 0);
  877. ControlArea->u.Flags.FilePointerNull = 1;
  878. if (AllocationAttributes & SEC_IMAGE) {
  879. MiRemoveImageSectionObject (File, ControlArea);
  880. }
  881. else {
  882. File->SectionObjectPointer->DataSectionObject = NULL;
  883. }
  884. ControlArea->u.Flags.BeingCreated = 0;
  885. UNLOCK_PFN (OldIrql);
  886. if (FileAcquired) {
  887. IoSetTopLevelIrp(NULL);
  888. FsRtlReleaseFile (File);
  889. }
  890. ExFreePool (NewControlArea);
  891. ObDereferenceObject (File);
  892. if (Event != NULL) {
  893. //
  894. // Signal any waiters that the segment structure exists.
  895. //
  896. KeSetEvent (&Event->Event, 0, FALSE);
  897. }
  898. return Status;
  899. }
  900. //
  901. // If the size was specified as zero, set the section size
  902. // from the created segment size. This solves problems with
  903. // race conditions when multiple sections
  904. // are created for the same mapped file with varying sizes.
  905. //
  906. if (*MaximumSize == 0) {
  907. Section.SizeOfSection.QuadPart = (LONGLONG)Segment->SizeOfSegment;
  908. }
  909. else {
  910. Section.SizeOfSection.QuadPart = (LONGLONG)*MaximumSize;
  911. }
  912. }
  913. }
  914. else {
  915. //
  916. // No file handle exists, this is a page file backed section.
  917. //
  918. if (AllocationAttributes & SEC_IMAGE) {
  919. return STATUS_INVALID_FILE_FOR_SECTION;
  920. }
  921. Status = MiCreatePagingFileMap (&NewSegment,
  922. MaximumSize,
  923. ProtectionMask,
  924. AllocationAttributes);
  925. if (!NT_SUCCESS(Status)) {
  926. return Status;
  927. }
  928. //
  929. // Set the section size from the created segment size. This
  930. // solves problems with race conditions when multiple sections
  931. // are created for the same mapped file with varying sizes.
  932. //
  933. Section.SizeOfSection.QuadPart = (LONGLONG)NewSegment->SizeOfSegment;
  934. ControlArea = NewSegment->ControlArea;
  935. //
  936. // Set IncrementedRefCount so any failures from this point before the
  937. // object is created will result in the control area & segment getting
  938. // torn down - otherwise these could leak. This is because pagefile
  939. // backed sections are not (and should not be) added to the
  940. // dereference segment cache.
  941. //
  942. IncrementedRefCount = 1;
  943. }
  944. if (NewSegment == NULL) {
  945. //
  946. // A new segment had to be created. Lock the PFN database and
  947. // check to see if any other threads also tried to create a new segment
  948. // for this file object at the same time.
  949. //
  950. NewSegment = Segment;
  951. SegmentControlArea = Segment->ControlArea;
  952. ASSERT (File != NULL);
  953. LOCK_PFN (OldIrql);
  954. Event = ControlArea->WaitingForDeletion;
  955. ControlArea->WaitingForDeletion = NULL;
  956. if (AllocationAttributes & SEC_IMAGE) {
  957. //
  958. // Change the control area in the file object pointer.
  959. //
  960. MiRemoveImageSectionObject (File, NewControlArea);
  961. MiInsertImageSectionObject (File, SegmentControlArea);
  962. ControlArea = SegmentControlArea;
  963. }
  964. else if (SegmentControlArea->u.Flags.Rom == 1) {
  965. ASSERT (File->SectionObjectPointer->DataSectionObject == NewControlArea);
  966. File->SectionObjectPointer->DataSectionObject = SegmentControlArea;
  967. ControlArea = SegmentControlArea;
  968. }
  969. ControlArea->u.Flags.BeingCreated = 0;
  970. UNLOCK_PFN (OldIrql);
  971. if ((AllocationAttributes & SEC_IMAGE) ||
  972. (SegmentControlArea->u.Flags.Rom == 1)) {
  973. //
  974. // Deallocate the pool used for the original control area.
  975. //
  976. ExFreePool (NewControlArea);
  977. }
  978. if (Event != NULL) {
  979. //
  980. // Signal any waiters that the segment structure exists.
  981. //
  982. KeSetEvent (&Event->Event, 0, FALSE);
  983. }
  984. PERFINFO_SECTION_CREATE(ControlArea);
  985. }
  986. //
  987. // Being created has now been cleared allowing other threads
  988. // to reference the segment. Release the resource on the file.
  989. //
  990. if (FileAcquired) {
  991. IoSetTopLevelIrp(NULL);
  992. FsRtlReleaseFile (File);
  993. FileAcquired = FALSE;
  994. }
  995. ReferenceObject:
  996. //
  997. // Now that the segment object is created, make the section object
  998. // refer to the segment object.
  999. //
  1000. Section.Segment = NewSegment;
  1001. Section.u.LongFlags = ControlArea->u.LongFlags;
  1002. //
  1003. // Update the count of writable user sections so the transaction semantics
  1004. // can be supported. Note that no lock synchronization is needed here as
  1005. // the transaction manager must already check for any open writable handles
  1006. // to the file - and no writable sections can be created without a writable
  1007. // file handle. So all that needs to be provided is a way for the
  1008. // transaction manager to know that there are lingering user views or
  1009. // created sections still open that have write access.
  1010. //
  1011. // This must be done before creating the object so a rogue user program
  1012. // that suspends this thread cannot subvert a transaction.
  1013. //
  1014. if ((FileObject == NULL) &&
  1015. (SectionPageProtection & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE)) &&
  1016. (ControlArea->u.Flags.Image == 0) &&
  1017. (ControlArea->FilePointer != NULL)) {
  1018. Section.u.Flags.UserWritable = 1;
  1019. InterlockedIncrement ((PLONG)&ControlArea->Segment->WritableUserReferences);
  1020. }
  1021. //
  1022. // Create the section object now. The section object is created
  1023. // now so that the error handling when the section object cannot
  1024. // be created is simplified.
  1025. //
  1026. if ((ControlArea->u.Flags.Image == 1) || (ControlArea->FilePointer == NULL)) {
  1027. PagedPoolCharge = sizeof (SECTION) + NewSegment->TotalNumberOfPtes * sizeof(MMPTE);
  1028. SubsectionSize = sizeof (SUBSECTION);
  1029. }
  1030. else {
  1031. PagedPoolCharge = 0;
  1032. SubsectionSize = sizeof (MSUBSECTION);
  1033. }
  1034. if ((ControlArea->u.Flags.GlobalOnlyPerSession == 0) &&
  1035. (ControlArea->u.Flags.Rom == 0)) {
  1036. NonPagedPoolCharge = sizeof (CONTROL_AREA);
  1037. Subsection = (PSUBSECTION)(ControlArea + 1);
  1038. }
  1039. else {
  1040. NonPagedPoolCharge = sizeof(LARGE_CONTROL_AREA);
  1041. Subsection = (PSUBSECTION)((PLARGE_CONTROL_AREA)ControlArea + 1);
  1042. }
  1043. do {
  1044. NonPagedPoolCharge += SubsectionSize;
  1045. Subsection = Subsection->NextSubsection;
  1046. } while (Subsection != NULL);
  1047. Status = ObCreateObject (PreviousMode,
  1048. MmSectionObjectType,
  1049. ObjectAttributes,
  1050. PreviousMode,
  1051. NULL,
  1052. sizeof(SECTION),
  1053. PagedPoolCharge,
  1054. NonPagedPoolCharge,
  1055. (PVOID *)&NewSection);
  1056. if (!NT_SUCCESS(Status)) {
  1057. if ((FileObject == NULL) &&
  1058. (SectionPageProtection & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE)) &&
  1059. (ControlArea->u.Flags.Image == 0) &&
  1060. (ControlArea->FilePointer != NULL)) {
  1061. ASSERT (Section.u.Flags.UserWritable == 1);
  1062. InterlockedDecrement ((PLONG)&ControlArea->Segment->WritableUserReferences);
  1063. }
  1064. goto UnrefAndReturn;
  1065. }
  1066. RtlCopyMemory (NewSection, &Section, sizeof(SECTION));
  1067. NewSection->Address.StartingVpn = 0;
  1068. if (!IgnoreFileSizing) {
  1069. //
  1070. // Indicate that the cache manager is not the owner of this
  1071. // section.
  1072. //
  1073. NewSection->u.Flags.UserReference = 1;
  1074. if (AllocationAttributes & SEC_NO_CHANGE) {
  1075. //
  1076. // Indicate that once the section is mapped, no protection
  1077. // changes or freeing the mapping is allowed.
  1078. //
  1079. NewSection->u.Flags.NoChange = 1;
  1080. }
  1081. if (((SectionPageProtection & PAGE_READWRITE) |
  1082. (SectionPageProtection & PAGE_EXECUTE_READWRITE)) == 0) {
  1083. //
  1084. // This section does not support WRITE access, indicate
  1085. // that changing the protection to WRITE results in COPY_ON_WRITE.
  1086. //
  1087. NewSection->u.Flags.CopyOnWrite = 1;
  1088. }
  1089. if (AllocationAttributes & SEC_BASED) {
  1090. NewSection->u.Flags.Based = 1;
  1091. SectionBasedRoot = &MmSectionBasedRoot;
  1092. //
  1093. // This section is based at a unique address system wide.
  1094. // Ensure it does not wrap the virtual address space as the
  1095. // SECTION structure would have to widen to accomodate this and
  1096. // it's not worth the performance penalty for the very few isolated
  1097. // cases that would want this. Note that sections larger than the
  1098. // address space can easily be created - it's just that beyond a
  1099. // certain point you shouldn't specify SEC_BASED (anything this big
  1100. // couldn't use a SEC_BASED section for anything anyway).
  1101. //
  1102. if ((UINT64)NewSection->SizeOfSection.QuadPart > (UINT64)MmHighSectionBase) {
  1103. ObDereferenceObject (NewSection);
  1104. return STATUS_NO_MEMORY;
  1105. }
  1106. #if defined(_WIN64)
  1107. SizeOfSection = NewSection->SizeOfSection.QuadPart;
  1108. #else
  1109. SizeOfSection = NewSection->SizeOfSection.LowPart;
  1110. #endif
  1111. //
  1112. // Get the allocation base mutex.
  1113. //
  1114. KeAcquireGuardedMutex (&MmSectionBasedMutex);
  1115. Status2 = MiFindEmptyAddressRangeDownBasedTree (
  1116. SizeOfSection,
  1117. MmHighSectionBase,
  1118. X64K,
  1119. SectionBasedRoot,
  1120. (PVOID *)&NewSection->Address.StartingVpn);
  1121. if (!NT_SUCCESS(Status2)) {
  1122. KeReleaseGuardedMutex (&MmSectionBasedMutex);
  1123. ObDereferenceObject (NewSection);
  1124. return Status2;
  1125. }
  1126. NewSection->Address.EndingVpn = NewSection->Address.StartingVpn +
  1127. SizeOfSection - 1;
  1128. MiInsertBasedSection (NewSection);
  1129. KeReleaseGuardedMutex (&MmSectionBasedMutex);
  1130. }
  1131. }
  1132. //
  1133. // If the cache manager is creating the section, set the was
  1134. // purged flag as the file size can change.
  1135. //
  1136. ControlArea->u.Flags.WasPurged |= IgnoreFileSizing;
  1137. //
  1138. // Check to see if the section is for a data file and the size
  1139. // of the section is greater than the current size of the
  1140. // segment.
  1141. //
  1142. if (((ControlArea->u.Flags.WasPurged == 1) && (!IgnoreFileSizing)) &&
  1143. (!FileSizeChecked)
  1144. ||
  1145. ((UINT64)NewSection->SizeOfSection.QuadPart >
  1146. NewSection->Segment->SizeOfSegment)) {
  1147. TempSectionSize = NewSection->SizeOfSection;
  1148. NewSection->SizeOfSection.QuadPart = (LONGLONG)NewSection->Segment->SizeOfSegment;
  1149. //
  1150. // Even if the caller didn't specify extension rights, we enable it here
  1151. // temporarily to make the section correct. Use a temporary section
  1152. // instead of temporarily editing the real section to avoid opening
  1153. // a security window that other concurrent threads could exploit.
  1154. //
  1155. if (((NewSection->InitialPageProtection & PAGE_READWRITE) |
  1156. (NewSection->InitialPageProtection & PAGE_EXECUTE_READWRITE)) == 0) {
  1157. SECTION WritableSection;
  1158. *(PSECTION)&WritableSection = *NewSection;
  1159. Status = MmExtendSection (&WritableSection,
  1160. &TempSectionSize,
  1161. IgnoreFileSizing);
  1162. NewSection->SizeOfSection = WritableSection.SizeOfSection;
  1163. }
  1164. else {
  1165. Status = MmExtendSection (NewSection,
  1166. &TempSectionSize,
  1167. IgnoreFileSizing);
  1168. }
  1169. if (!NT_SUCCESS(Status)) {
  1170. ObDereferenceObject (NewSection);
  1171. return Status;
  1172. }
  1173. }
  1174. *SectionObject = (PVOID)NewSection;
  1175. return Status;
  1176. UnrefAndReturn:
  1177. //
  1178. // Unreference the control area, if it was referenced and return
  1179. // the error status.
  1180. //
  1181. if (FileAcquired) {
  1182. IoSetTopLevelIrp(NULL);
  1183. FsRtlReleaseFile (File);
  1184. }
  1185. if (IncrementedRefCount) {
  1186. LOCK_PFN (OldIrql);
  1187. ControlArea->NumberOfSectionReferences -= 1;
  1188. if (!IgnoreFileSizing) {
  1189. ASSERT ((LONG)ControlArea->NumberOfUserReferences > 0);
  1190. ControlArea->NumberOfUserReferences -= 1;
  1191. }
  1192. MiCheckControlArea (ControlArea, NULL, OldIrql);
  1193. }
  1194. return Status;
  1195. }
  1196. LOGICAL
  1197. MiMakeControlAreaRom (
  1198. IN PFILE_OBJECT File,
  1199. IN PLARGE_CONTROL_AREA ControlArea,
  1200. IN PFN_NUMBER PageFrameNumber
  1201. )
  1202. /*++
  1203. Routine Description:
  1204. This function marks the control area as ROM-backed. It can fail if the
  1205. parallel control area (image vs data) is currently active as ROM-backed
  1206. as the same PFNs cannot be used for both control areas simultaneously.
  1207. Arguments:
  1208. ControlArea - Supplies the relevant control area.
  1209. PageFrameNumber - Supplies the starting physical page frame number.
  1210. Return Value:
  1211. TRUE if the control area was marked as ROM-backed, FALSE if not.
  1212. --*/
  1213. {
  1214. LOGICAL ControlAreaMarked;
  1215. PCONTROL_AREA OtherControlArea;
  1216. KIRQL OldIrql;
  1217. ControlAreaMarked = FALSE;
  1218. LOCK_PFN (OldIrql);
  1219. if (ControlArea->u.Flags.Image == 1) {
  1220. OtherControlArea = (PCONTROL_AREA) File->SectionObjectPointer->DataSectionObject;
  1221. }
  1222. else {
  1223. OtherControlArea = (PCONTROL_AREA) File->SectionObjectPointer->ImageSectionObject;
  1224. }
  1225. //
  1226. // This could be made smarter (ie: throw away the other control area if it's
  1227. // not in use) but for now, keep it simple.
  1228. //
  1229. if ((OtherControlArea == NULL) || (OtherControlArea->u.Flags.Rom == 0)) {
  1230. ControlArea->u.Flags.Rom = 1;
  1231. ControlArea->StartingFrame = PageFrameNumber;
  1232. ControlAreaMarked = TRUE;
  1233. }
  1234. UNLOCK_PFN (OldIrql);
  1235. return ControlAreaMarked;
  1236. }
  1237. ULONG MiImageFailure;
  1238. #define MI_BAD_IMAGE(x) MiImageFailure = x;
  1239. NTSTATUS
  1240. MiCreateImageFileMap (
  1241. IN PFILE_OBJECT File,
  1242. OUT PSEGMENT *Segment
  1243. )
  1244. /*++
  1245. Routine Description:
  1246. This function creates the necessary structures to allow the mapping
  1247. of an image file.
  1248. The image file is opened and verified for correctness, a segment
  1249. object is created and initialized based on data in the image
  1250. header.
  1251. Arguments:
  1252. File - Supplies the file object for the image file.
  1253. Segment - Returns the segment object.
  1254. Return Value:
  1255. NTSTATUS.
  1256. --*/
  1257. {
  1258. PMMPFN Pfn1;
  1259. PMMPFN Pfn2;
  1260. LOGICAL CheckSplitPages;
  1261. LOGICAL MarkModified;
  1262. LOGICAL SingleSubsection;
  1263. NTSTATUS Status;
  1264. ULONG_PTR EndingAddress;
  1265. PFN_NUMBER NumberOfPtes;
  1266. SIZE_T SizeOfSegment;
  1267. ULONG SectionVirtualSize;
  1268. ULONG SizeOfRawData;
  1269. ULONG i;
  1270. ULONG j;
  1271. PCONTROL_AREA ControlArea;
  1272. PSUBSECTION Subsection;
  1273. PMMPTE PointerPte;
  1274. MMPTE TempPte;
  1275. MMPTE TempPteDemandZero;
  1276. PVOID Base;
  1277. PIMAGE_DOS_HEADER DosHeader;
  1278. PIMAGE_NT_HEADERS NtHeader;
  1279. PIMAGE_FILE_HEADER FileHeader;
  1280. ULONG SizeOfImage;
  1281. ULONG SizeOfHeaders;
  1282. #if defined (_WIN64)
  1283. PIMAGE_NT_HEADERS32 NtHeader32;
  1284. #endif
  1285. PIMAGE_DATA_DIRECTORY ComPlusDirectoryEntry;
  1286. PIMAGE_SECTION_HEADER SectionTableEntry;
  1287. PSEGMENT NewSegment;
  1288. ULONG SectorOffset;
  1289. ULONG NumberOfSubsections;
  1290. PFN_NUMBER PageFrameNumber;
  1291. PFN_NUMBER XipFrameNumber;
  1292. LOGICAL XipFile;
  1293. LOGICAL GlobalPerSession;
  1294. LARGE_INTEGER StartingOffset;
  1295. PCHAR ExtendedHeader;
  1296. PPFN_NUMBER Page;
  1297. ULONG_PTR PreferredImageBase;
  1298. ULONG_PTR NextVa;
  1299. ULONG_PTR ImageBase;
  1300. KEVENT InPageEvent;
  1301. PMDL Mdl;
  1302. ULONG ImageFileSize;
  1303. ULONG OffsetToSectionTable;
  1304. ULONG ImageAlignment;
  1305. ULONG RoundingAlignment;
  1306. ULONG FileAlignment;
  1307. LOGICAL ImageCommit;
  1308. LOGICAL SectionCommit;
  1309. IO_STATUS_BLOCK IoStatus;
  1310. LARGE_INTEGER EndOfFile;
  1311. ULONG NtHeaderSize;
  1312. ULONG SubsectionsAllocated;
  1313. PLARGE_CONTROL_AREA LargeControlArea;
  1314. PSUBSECTION NewSubsection;
  1315. ULONG OriginalProtection;
  1316. ULONG LoaderFlags;
  1317. ULONG TempNumberOfSubsections;
  1318. PIMAGE_SECTION_HEADER TempSectionTableEntry;
  1319. ULONG AdditionalSubsections;
  1320. ULONG AdditionalPtes;
  1321. ULONG AdditionalBasePtes;
  1322. ULONG NewSubsectionsAllocated;
  1323. PSEGMENT OldSegment;
  1324. PMMPTE NewPointerPte;
  1325. PMMPTE OldPointerPte;
  1326. PFN_NUMBER OrigNumberOfPtes;
  1327. PCONTROL_AREA NewControlArea;
  1328. SIZE_T CommitCharged;
  1329. PHYSICAL_ADDRESS PhysicalAddress;
  1330. LOGICAL ActiveDataReferences;
  1331. PFN_NUMBER StackMdl[(sizeof(MDL)/sizeof(PFN_NUMBER)) + MM_MAXIMUM_IMAGE_HEADER / PAGE_SIZE];
  1332. PMMPFN ImagePages;
  1333. #if defined (_MIALT4K_)
  1334. ULONG ReadCount;
  1335. ULONG RawDataSize;
  1336. ULONG ReadSize;
  1337. PVOID HalfPage;
  1338. PHYSICAL_ADDRESS HalfPagePhysicalAddress;
  1339. PVOID StraddleVa;
  1340. MMPTE PteContents;
  1341. PMMPTE PreviousPte;
  1342. PMMPTE ReadPte;
  1343. PFN_NUMBER HalfPageFrameNumber;
  1344. PFN_NUMBER StraddleFrameNumber;
  1345. PSUBSECTION ReadSubsection;
  1346. ULONG PreviousSectionCharacteristics;
  1347. LARGE_INTEGER TempOffset;
  1348. #endif
  1349. #if defined (_IA64_)
  1350. LOGICAL InvalidAlignmentAllowed;
  1351. InvalidAlignmentAllowed = FALSE;
  1352. #else
  1353. #define InvalidAlignmentAllowed FALSE
  1354. #endif
  1355. PAGED_CODE ();
  1356. Status = FsRtlGetFileSize (File, &EndOfFile);
  1357. if (!NT_SUCCESS (Status)) {
  1358. MI_BAD_IMAGE (1);
  1359. if (Status == STATUS_FILE_IS_A_DIRECTORY) {
  1360. //
  1361. // Can't map a directory as a section - return an error.
  1362. //
  1363. return STATUS_INVALID_FILE_FOR_SECTION;
  1364. }
  1365. return Status;
  1366. }
  1367. if (EndOfFile.HighPart != 0) {
  1368. //
  1369. // File too big. Return error.
  1370. //
  1371. return STATUS_INVALID_FILE_FOR_SECTION;
  1372. }
  1373. CommitCharged = 0;
  1374. ExtendedHeader = NULL;
  1375. ControlArea = NULL;
  1376. NewSegment = NULL;
  1377. Base = NULL;
  1378. //
  1379. // Initializing IoStatus.Information is not needed for correctness, but
  1380. // without it the compiler cannot compile this code W4 to check
  1381. // for use of uninitialized variables.
  1382. //
  1383. IoStatus.Information = 0;
  1384. Mdl = (PMDL) &StackMdl;
  1385. //
  1386. // Read in the file header.
  1387. //
  1388. // Create an event and MDL for the read operation.
  1389. //
  1390. KeInitializeEvent (&InPageEvent, NotificationEvent, FALSE);
  1391. MmInitializeMdl (Mdl, NULL, PAGE_SIZE);
  1392. Mdl->MdlFlags |= MDL_PAGES_LOCKED;
  1393. PageFrameNumber = MiGetPageForHeader (TRUE);
  1394. ASSERT (PageFrameNumber != 0);
  1395. Pfn1 = MI_PFN_ELEMENT (PageFrameNumber);
  1396. ASSERT (Pfn1->u1.Flink == 0);
  1397. ImagePages = Pfn1;
  1398. Page = (PPFN_NUMBER)(Mdl + 1);
  1399. *Page = PageFrameNumber;
  1400. ZERO_LARGE (StartingOffset);
  1401. CcZeroEndOfLastPage (File);
  1402. //
  1403. // Flush the data section if there is one.
  1404. //
  1405. // At the same time, capture whether there are any references to the
  1406. // data control area. If so, flip the pages from the image control
  1407. // area into pagefile backings to prevent anyone else's data writes
  1408. // from changing the file after it is validated (this can happen if the
  1409. // pages from the image control area need to be re-paged in later).
  1410. //
  1411. ActiveDataReferences = MiFlushDataSection (File);
  1412. Base = MiCopyHeaderIfResident (File, PageFrameNumber);
  1413. if (Base == NULL) {
  1414. ASSERT (Mdl->MdlFlags & MDL_PAGES_LOCKED);
  1415. Status = IoPageRead (File,
  1416. Mdl,
  1417. &StartingOffset,
  1418. &InPageEvent,
  1419. &IoStatus);
  1420. if (Status == STATUS_PENDING) {
  1421. KeWaitForSingleObject (&InPageEvent,
  1422. WrPageIn,
  1423. KernelMode,
  1424. FALSE,
  1425. NULL);
  1426. Status = IoStatus.Status;
  1427. }
  1428. if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) {
  1429. MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
  1430. }
  1431. if (!NT_SUCCESS(Status)) {
  1432. MI_BAD_IMAGE (2);
  1433. if ((Status != STATUS_FILE_LOCK_CONFLICT) && (Status != STATUS_FILE_IS_OFFLINE)) {
  1434. Status = STATUS_INVALID_FILE_FOR_SECTION;
  1435. }
  1436. goto BadPeImageSegment;
  1437. }
  1438. Base = MiMapImageHeaderInHyperSpace (PageFrameNumber);
  1439. if (IoStatus.Information != PAGE_SIZE) {
  1440. //
  1441. // A full page was not read from the file, zero any remaining
  1442. // bytes.
  1443. //
  1444. RtlZeroMemory ((PVOID)((PCHAR)Base + IoStatus.Information),
  1445. PAGE_SIZE - IoStatus.Information);
  1446. }
  1447. }
  1448. DosHeader = (PIMAGE_DOS_HEADER) Base;
  1449. //
  1450. // Check to determine if this is an NT image (PE format) or
  1451. // a DOS image, Win-16 image, or OS/2 image. If the image is
  1452. // not NT format, return an error indicating which image it
  1453. // appears to be.
  1454. //
  1455. if (DosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
  1456. Status = STATUS_INVALID_IMAGE_NOT_MZ;
  1457. #if 0
  1458. MI_BAD_IMAGE (3); // Don't log this as it happens all the time.
  1459. #endif
  1460. goto BadPeImageSegment;
  1461. }
  1462. #ifndef i386
  1463. if (((ULONG)DosHeader->e_lfanew & 3) != 0) {
  1464. //
  1465. // The image header is not aligned on a longword boundary.
  1466. // Report this as an invalid protected mode image.
  1467. //
  1468. Status = STATUS_INVALID_IMAGE_PROTECT;
  1469. MI_BAD_IMAGE (4);
  1470. goto BadPeImageSegment;
  1471. }
  1472. #endif
  1473. if ((ULONG)DosHeader->e_lfanew > EndOfFile.LowPart) {
  1474. Status = STATUS_INVALID_IMAGE_PROTECT;
  1475. MI_BAD_IMAGE (5);
  1476. goto BadPeImageSegment;
  1477. }
  1478. if (((ULONG)DosHeader->e_lfanew +
  1479. sizeof(IMAGE_NT_HEADERS) +
  1480. (16 * sizeof(IMAGE_SECTION_HEADER))) <= (ULONG)DosHeader->e_lfanew) {
  1481. Status = STATUS_INVALID_IMAGE_PROTECT;
  1482. MI_BAD_IMAGE (6);
  1483. goto BadPeImageSegment;
  1484. }
  1485. if (((ULONG)DosHeader->e_lfanew +
  1486. sizeof(IMAGE_NT_HEADERS) +
  1487. (16 * sizeof(IMAGE_SECTION_HEADER))) > PAGE_SIZE) {
  1488. //
  1489. // The PE header is not within the page already read or the
  1490. // objects are in another page.
  1491. // Build another MDL and read an additional 8k.
  1492. //
  1493. ExtendedHeader = ExAllocatePoolWithTag (NonPagedPool,
  1494. MM_MAXIMUM_IMAGE_HEADER,
  1495. MMTEMPORARY);
  1496. if (ExtendedHeader == NULL) {
  1497. Status = STATUS_INSUFFICIENT_RESOURCES;
  1498. MI_BAD_IMAGE (7);
  1499. goto BadPeImageSegment;
  1500. }
  1501. //
  1502. // Build an MDL for the operation.
  1503. //
  1504. MmInitializeMdl (Mdl, ExtendedHeader, MM_MAXIMUM_IMAGE_HEADER);
  1505. MmBuildMdlForNonPagedPool (Mdl);
  1506. StartingOffset.LowPart = PtrToUlong(PAGE_ALIGN ((ULONG)DosHeader->e_lfanew));
  1507. KeClearEvent (&InPageEvent);
  1508. Status = IoPageRead (File,
  1509. Mdl,
  1510. &StartingOffset,
  1511. &InPageEvent,
  1512. &IoStatus);
  1513. if (Status == STATUS_PENDING) {
  1514. KeWaitForSingleObject (&InPageEvent,
  1515. WrPageIn,
  1516. KernelMode,
  1517. FALSE,
  1518. NULL);
  1519. Status = IoStatus.Status;
  1520. }
  1521. if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) {
  1522. MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
  1523. }
  1524. if (!NT_SUCCESS(Status)) {
  1525. MI_BAD_IMAGE (8);
  1526. if ((Status != STATUS_FILE_LOCK_CONFLICT) && (Status != STATUS_FILE_IS_OFFLINE)) {
  1527. Status = STATUS_INVALID_FILE_FOR_SECTION;
  1528. }
  1529. goto BadPeImageSegment;
  1530. }
  1531. NtHeader = (PIMAGE_NT_HEADERS)((PCHAR)ExtendedHeader +
  1532. BYTE_OFFSET((ULONG)DosHeader->e_lfanew));
  1533. NtHeaderSize = MM_MAXIMUM_IMAGE_HEADER -
  1534. (ULONG)(BYTE_OFFSET((ULONG)DosHeader->e_lfanew));
  1535. }
  1536. else {
  1537. NtHeader = (PIMAGE_NT_HEADERS)((PCHAR)DosHeader +
  1538. (ULONG)DosHeader->e_lfanew);
  1539. NtHeaderSize = PAGE_SIZE - (ULONG)DosHeader->e_lfanew;
  1540. }
  1541. FileHeader = &NtHeader->FileHeader;
  1542. //
  1543. // Check to see if this is an NT image or a DOS or OS/2 image.
  1544. //
  1545. Status = MiVerifyImageHeader (NtHeader, DosHeader, NtHeaderSize);
  1546. if (Status != STATUS_SUCCESS) {
  1547. MI_BAD_IMAGE (9);
  1548. goto BadPeImageSegment;
  1549. }
  1550. CheckSplitPages = FALSE;
  1551. #if defined(_WIN64)
  1552. if (NtHeader->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
  1553. NtHeader32 = NULL;
  1554. #endif
  1555. ImageAlignment = NtHeader->OptionalHeader.SectionAlignment;
  1556. FileAlignment = NtHeader->OptionalHeader.FileAlignment - 1;
  1557. SizeOfImage = NtHeader->OptionalHeader.SizeOfImage;
  1558. LoaderFlags = NtHeader->OptionalHeader.LoaderFlags;
  1559. ImageBase = NtHeader->OptionalHeader.ImageBase;
  1560. SizeOfHeaders = NtHeader->OptionalHeader.SizeOfHeaders;
  1561. //
  1562. // Read in the COM+ directory entry.
  1563. //
  1564. ComPlusDirectoryEntry = &NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR];
  1565. #if defined (_WIN64)
  1566. }
  1567. else {
  1568. //
  1569. // The image is 32-bit. All code below this point must check
  1570. // if NtHeader is NULL. If it is, then the image is PE32 and
  1571. // NtHeader32 must be used.
  1572. //
  1573. NtHeader32 = (PIMAGE_NT_HEADERS32) NtHeader;
  1574. NtHeader = NULL;
  1575. ImageAlignment = NtHeader32->OptionalHeader.SectionAlignment;
  1576. FileAlignment = NtHeader32->OptionalHeader.FileAlignment - 1;
  1577. SizeOfImage = NtHeader32->OptionalHeader.SizeOfImage;
  1578. LoaderFlags = NtHeader32->OptionalHeader.LoaderFlags;
  1579. ImageBase = NtHeader32->OptionalHeader.ImageBase;
  1580. SizeOfHeaders = NtHeader32->OptionalHeader.SizeOfHeaders;
  1581. //
  1582. // Read in the COM+ directory entry.
  1583. //
  1584. ComPlusDirectoryEntry = &NtHeader32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR];
  1585. #if defined (_MIALT4K_)
  1586. if ((ExtendedHeader == NULL) && (ImageAlignment == PAGE_4K)) {
  1587. //
  1588. // The DOS header, NT header and the section headers all fit in
  1589. // the first page. Check all the section headers now and if none
  1590. // of them indicate unaligned global subsections then map this
  1591. // image with individual subsections, splitting page permissions
  1592. // and boundaries as needed.
  1593. //
  1594. OffsetToSectionTable = sizeof(ULONG) +
  1595. sizeof(IMAGE_FILE_HEADER) +
  1596. FileHeader->SizeOfOptionalHeader;
  1597. if (BYTE_OFFSET (NtHeader32) + OffsetToSectionTable +
  1598. (16 * sizeof (IMAGE_SECTION_HEADER)) <= PAGE_SIZE) {
  1599. SectionTableEntry = (PIMAGE_SECTION_HEADER)((PCHAR)NtHeader32 +
  1600. OffsetToSectionTable);
  1601. NumberOfSubsections = FileHeader->NumberOfSections;
  1602. //
  1603. // If any global subsection is unaligned then map the image
  1604. // with a single subsection. We could get clever here if/when
  1605. // we encounter an unaligned global subsection, by seeing if
  1606. // following (n consecutive) subsections are also global and
  1607. // the last one ends on an aligned boundary - and if so,
  1608. // still run the image natively, but it's probably unlikely
  1609. // that any image has more than one global subsection.
  1610. //
  1611. CheckSplitPages = TRUE;
  1612. while (NumberOfSubsections > 0) {
  1613. if ((SectionTableEntry->Characteristics & IMAGE_SCN_MEM_SHARED) &&
  1614. ((BYTE_OFFSET (SectionTableEntry->VirtualAddress) != 0) ||
  1615. (BYTE_OFFSET (SectionTableEntry->VirtualAddress + SectionTableEntry->Misc.VirtualSize) <= PAGE_4K))) {
  1616. CheckSplitPages = FALSE;
  1617. break;
  1618. }
  1619. SectionTableEntry += 1;
  1620. NumberOfSubsections -= 1;
  1621. }
  1622. }
  1623. }
  1624. #endif
  1625. }
  1626. #endif
  1627. NumberOfPtes = BYTES_TO_PAGES (SizeOfImage);
  1628. if (NumberOfPtes == 0) {
  1629. Status = STATUS_INVALID_IMAGE_FORMAT;
  1630. MI_BAD_IMAGE (0xA);
  1631. goto BadPeImageSegment;
  1632. }
  1633. #if defined (_WIN64)
  1634. if (NumberOfPtes >= _4gb) {
  1635. Status = STATUS_INVALID_IMAGE_FORMAT;
  1636. MI_BAD_IMAGE (0xB);
  1637. goto BadPeImageSegment;
  1638. }
  1639. #endif
  1640. //
  1641. // Set the appropriate bit for .NET images inside the image's
  1642. // section loader flags.
  1643. //
  1644. if ((ComPlusDirectoryEntry->VirtualAddress != 0) &&
  1645. (ComPlusDirectoryEntry->Size != 0)) {
  1646. LoaderFlags |= IMAGE_LOADER_FLAGS_COMPLUS;
  1647. }
  1648. RoundingAlignment = ImageAlignment;
  1649. NumberOfSubsections = FileHeader->NumberOfSections;
  1650. if ((ImageAlignment >= PAGE_SIZE) || (CheckSplitPages == TRUE)) {
  1651. //
  1652. // Allocate a control area and a subsection for each section
  1653. // header plus one for the image header which has no section.
  1654. //
  1655. SubsectionsAllocated = NumberOfSubsections + 1;
  1656. ControlArea = ExAllocatePoolWithTag (NonPagedPool,
  1657. sizeof(CONTROL_AREA) +
  1658. (sizeof(SUBSECTION) *
  1659. SubsectionsAllocated),
  1660. 'iCmM');
  1661. SingleSubsection = FALSE;
  1662. }
  1663. else {
  1664. //
  1665. // The image alignment is less than the page size,
  1666. // map the image with a single subsection.
  1667. //
  1668. ControlArea = ExAllocatePoolWithTag (NonPagedPool,
  1669. sizeof(CONTROL_AREA) + sizeof(SUBSECTION),
  1670. MMCONTROL);
  1671. SubsectionsAllocated = 1;
  1672. SingleSubsection = TRUE;
  1673. }
  1674. if (ControlArea == NULL) {
  1675. //
  1676. // The requested pool could not be allocated.
  1677. //
  1678. Status = STATUS_INSUFFICIENT_RESOURCES;
  1679. MI_BAD_IMAGE (0xC);
  1680. goto BadPeImageSegment;
  1681. }
  1682. //
  1683. // Zero the control area and the FIRST subsection.
  1684. //
  1685. RtlZeroMemory (ControlArea, sizeof(CONTROL_AREA) + sizeof(SUBSECTION));
  1686. ASSERT (ControlArea->u.Flags.GlobalOnlyPerSession == 0);
  1687. Subsection = (PSUBSECTION)(ControlArea + 1);
  1688. #if defined (_IA64_)
  1689. if (ImageAlignment < PAGE_SIZE) {
  1690. if ((FileHeader->Machine < USER_SHARED_DATA->ImageNumberLow) ||
  1691. (FileHeader->Machine > USER_SHARED_DATA->ImageNumberHigh)) {
  1692. if (CheckSplitPages == FALSE) {
  1693. if (KeGetPreviousMode() != KernelMode) {
  1694. InvalidAlignmentAllowed = TRUE;
  1695. }
  1696. else {
  1697. //
  1698. // Don't allow kernel prefetch of wow images on IA64
  1699. // because these images might have shared subsections
  1700. // which are not handled properly. The right fix for
  1701. // this is to combine image section creation for both
  1702. // prefetch (kernel) and non-prefetch (user) callers.
  1703. // However, we only found this just before shipping so
  1704. // this is risky as test coverage is light. The worst
  1705. // implication of this disabling is just lower performance
  1706. // but is not a correctness issue.
  1707. //
  1708. Status = STATUS_INVALID_IMAGE_PROTECT;
  1709. MI_BAD_IMAGE (0x25);
  1710. goto BadPeImageSegment;
  1711. }
  1712. }
  1713. }
  1714. }
  1715. OrigNumberOfPtes = NumberOfPtes;
  1716. #endif
  1717. SizeOfSegment = sizeof(SEGMENT) + (sizeof(MMPTE) * ((ULONG)NumberOfPtes - 1)) +
  1718. sizeof(SECTION_IMAGE_INFORMATION);
  1719. NewSegment = ExAllocatePoolWithTag (PagedPool | POOL_MM_ALLOCATION,
  1720. SizeOfSegment,
  1721. MMSECT);
  1722. if (NewSegment == NULL) {
  1723. //
  1724. // The requested pool could not be allocated.
  1725. //
  1726. MI_BAD_IMAGE (0xD);
  1727. Status = STATUS_INSUFFICIENT_RESOURCES;
  1728. goto BadPeImageSegment;
  1729. }
  1730. *Segment = NewSegment;
  1731. RtlZeroMemory (NewSegment, sizeof(SEGMENT));
  1732. NewSegment->PrototypePte = &NewSegment->ThePtes[0];
  1733. PointerPte = &NewSegment->ThePtes[0];
  1734. Pfn1->u2.Blink = (PFN_NUMBER) PointerPte;
  1735. NewSegment->ControlArea = ControlArea;
  1736. NewSegment->u2.ImageInformation =
  1737. (PSECTION_IMAGE_INFORMATION)((PCHAR)NewSegment + sizeof(SEGMENT) +
  1738. (sizeof(MMPTE) * (NumberOfPtes - 1)));
  1739. NewSegment->TotalNumberOfPtes = (ULONG) NumberOfPtes;
  1740. NewSegment->NonExtendedPtes = (ULONG) NumberOfPtes;
  1741. NewSegment->SizeOfSegment = NumberOfPtes * PAGE_SIZE;
  1742. RtlZeroMemory (NewSegment->u2.ImageInformation,
  1743. sizeof (SECTION_IMAGE_INFORMATION));
  1744. #if DBG
  1745. //
  1746. // Zero fill the prototype PTEs so they can be checked in the error
  1747. // path to make sure no valid entries got left behind.
  1748. //
  1749. if (NumberOfPtes != 0) {
  1750. MiZeroMemoryPte (PointerPte, NumberOfPtes);
  1751. }
  1752. #endif
  1753. //
  1754. // This code is built twice on the Win64 build - once for PE32+ and once for
  1755. // PE32 images.
  1756. //
  1757. #define INIT_IMAGE_INFORMATION(OptHdr) { \
  1758. NewSegment->u2.ImageInformation->TransferAddress = \
  1759. (PVOID)((ULONG_PTR)((OptHdr).ImageBase) + \
  1760. (OptHdr).AddressOfEntryPoint); \
  1761. NewSegment->u2.ImageInformation->MaximumStackSize = \
  1762. (OptHdr).SizeOfStackReserve; \
  1763. NewSegment->u2.ImageInformation->CommittedStackSize = \
  1764. (OptHdr).SizeOfStackCommit; \
  1765. NewSegment->u2.ImageInformation->SubSystemType = \
  1766. (OptHdr).Subsystem; \
  1767. NewSegment->u2.ImageInformation->SubSystemMajorVersion = (USHORT)((OptHdr).MajorSubsystemVersion); \
  1768. NewSegment->u2.ImageInformation->SubSystemMinorVersion = (USHORT)((OptHdr).MinorSubsystemVersion); \
  1769. NewSegment->u2.ImageInformation->DllCharacteristics = \
  1770. (OptHdr).DllCharacteristics; \
  1771. NewSegment->u2.ImageInformation->ImageContainsCode = \
  1772. (BOOLEAN)(((OptHdr).SizeOfCode != 0) || \
  1773. ((OptHdr).AddressOfEntryPoint != 0)); \
  1774. }
  1775. #if defined (_WIN64)
  1776. if (NtHeader) {
  1777. #endif
  1778. INIT_IMAGE_INFORMATION(NtHeader->OptionalHeader);
  1779. #if defined (_WIN64)
  1780. }
  1781. else {
  1782. //
  1783. // The image is 32-bit so use the 32-bit header
  1784. //
  1785. INIT_IMAGE_INFORMATION(NtHeader32->OptionalHeader);
  1786. }
  1787. #endif
  1788. #undef INIT_IMAGE_INFORMATION
  1789. NewSegment->u2.ImageInformation->ImageCharacteristics =
  1790. FileHeader->Characteristics;
  1791. NewSegment->u2.ImageInformation->Machine = FileHeader->Machine;
  1792. NewSegment->u2.ImageInformation->LoaderFlags = LoaderFlags;
  1793. ControlArea->Segment = NewSegment;
  1794. ControlArea->NumberOfSectionReferences = 1;
  1795. ControlArea->NumberOfUserReferences = 1;
  1796. ControlArea->u.Flags.BeingCreated = 1;
  1797. ControlArea->u.Flags.Image = 1;
  1798. ControlArea->u.Flags.File = 1;
  1799. if ((ActiveDataReferences == TRUE) ||
  1800. (IoIsDeviceEjectable(File->DeviceObject)) ||
  1801. ((FileHeader->Characteristics &
  1802. IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP) &&
  1803. (FILE_REMOVABLE_MEDIA & File->DeviceObject->Characteristics)) ||
  1804. ((FileHeader->Characteristics &
  1805. IMAGE_FILE_NET_RUN_FROM_SWAP) &&
  1806. (FILE_REMOTE_DEVICE & File->DeviceObject->Characteristics))) {
  1807. //
  1808. // This file resides on a floppy disk or a removable media or
  1809. // network with flags set indicating it should be copied
  1810. // to the paging file.
  1811. //
  1812. ControlArea->u.Flags.FloppyMedia = 1;
  1813. }
  1814. #if DBG
  1815. if (MiMakeImageFloppy[0] & 0x1) {
  1816. MiMakeImageFloppy[1] += 1;
  1817. ControlArea->u.Flags.FloppyMedia = 1;
  1818. }
  1819. #endif
  1820. if (FILE_REMOTE_DEVICE & File->DeviceObject->Characteristics) {
  1821. //
  1822. // This file resides on a redirected drive.
  1823. //
  1824. ControlArea->u.Flags.Networked = 1;
  1825. }
  1826. ControlArea->FilePointer = File;
  1827. //
  1828. // Build the subsection and prototype PTEs for the image header.
  1829. //
  1830. Subsection->ControlArea = ControlArea;
  1831. NextVa = ImageBase;
  1832. #if defined (_WIN64)
  1833. //
  1834. // Don't let bogus headers cause system alignment faults.
  1835. //
  1836. if (FileHeader->SizeOfOptionalHeader & (sizeof (ULONG_PTR) - 1)) {
  1837. Status = STATUS_INVALID_IMAGE_FORMAT;
  1838. MI_BAD_IMAGE (0xE);
  1839. goto BadPeImageSegment;
  1840. }
  1841. #endif
  1842. if ((NextVa & (X64K - 1)) != 0) {
  1843. //
  1844. // Image header is not aligned on a 64k boundary.
  1845. //
  1846. Status = STATUS_INVALID_IMAGE_FORMAT;
  1847. MI_BAD_IMAGE (0xF);
  1848. goto BadPeImageSegment;
  1849. }
  1850. NewSegment->BasedAddress = (PVOID) NextVa;
  1851. #if DBG
  1852. if (NextVa != 0 && NextVa == MiMatchSectionBase) {
  1853. DbgPrint ("MM: NewSegment %p being created\n", NewSegment);
  1854. DbgBreakPoint ();
  1855. }
  1856. #endif
  1857. if (SizeOfHeaders >= SizeOfImage) {
  1858. Status = STATUS_INVALID_IMAGE_FORMAT;
  1859. MI_BAD_IMAGE (0x10);
  1860. goto BadPeImageSegment;
  1861. }
  1862. if (CheckSplitPages == FALSE) {
  1863. Subsection->PtesInSubsection = MI_ROUND_TO_SIZE (SizeOfHeaders, ImageAlignment) >> PAGE_SHIFT;
  1864. }
  1865. else {
  1866. Subsection->PtesInSubsection =
  1867. (ULONG) MI_COMPUTE_PAGES_SPANNED (0,
  1868. MI_ROUND_TO_SIZE (SizeOfHeaders, ImageAlignment));
  1869. }
  1870. PointerPte = NewSegment->PrototypePte;
  1871. Subsection->SubsectionBase = PointerPte;
  1872. TempPte.u.Long = MiGetSubsectionAddressForPte (Subsection);
  1873. TempPte.u.Soft.Prototype = 1;
  1874. NewSegment->SegmentPteTemplate = TempPte;
  1875. SectorOffset = 0;
  1876. if (SingleSubsection == TRUE) {
  1877. //
  1878. // The image is aligned on less than a page size boundary.
  1879. // Initialize the single subsection to refer to the image.
  1880. //
  1881. PointerPte = NewSegment->PrototypePte;
  1882. Subsection->PtesInSubsection = (ULONG) NumberOfPtes;
  1883. #if !defined (_WIN64)
  1884. //
  1885. // Note this only needs to be checked for 32-bit systems as NT64
  1886. // does a much more extensive reallocation of images that are built
  1887. // with alignment less than PAGE_SIZE.
  1888. //
  1889. if ((UINT64)SizeOfImage < (UINT64)EndOfFile.QuadPart) {
  1890. //
  1891. // Images that have a size of image (according to the header) that's
  1892. // smaller than the actual file only get that many prototype PTEs.
  1893. // Initialize the subsection properly so no one can read off the
  1894. // end as that would corrupt the PFN database element's original
  1895. // PTE entry.
  1896. //
  1897. Subsection->NumberOfFullSectors = (SizeOfImage >> MMSECTOR_SHIFT);
  1898. Subsection->u.SubsectionFlags.SectorEndOffset =
  1899. SizeOfImage & MMSECTOR_MASK;
  1900. }
  1901. else {
  1902. #endif
  1903. Subsection->NumberOfFullSectors =
  1904. (ULONG)(EndOfFile.QuadPart >> MMSECTOR_SHIFT);
  1905. ASSERT ((ULONG)(EndOfFile.HighPart & 0xFFFFF000) == 0);
  1906. Subsection->u.SubsectionFlags.SectorEndOffset =
  1907. EndOfFile.LowPart & MMSECTOR_MASK;
  1908. #if !defined (_WIN64)
  1909. }
  1910. #endif
  1911. Subsection->u.SubsectionFlags.Protection = MM_EXECUTE_WRITECOPY;
  1912. //
  1913. // Set all the PTEs to the execute-read-write protection.
  1914. // The section will control access to these and the segment
  1915. // must provide a method to allow other users to map the file
  1916. // for various protections.
  1917. //
  1918. TempPte.u.Soft.Protection = MM_EXECUTE_WRITECOPY;
  1919. NewSegment->SegmentPteTemplate = TempPte;
  1920. //
  1921. // Invalid image alignments are supported for cross platform
  1922. // emulation. Only IA64 requires extra handling because
  1923. // the native page size is larger than x86.
  1924. //
  1925. if (InvalidAlignmentAllowed == TRUE) {
  1926. TempPteDemandZero.u.Long = 0;
  1927. TempPteDemandZero.u.Soft.Protection = MM_EXECUTE_WRITECOPY;
  1928. SectorOffset = 0;
  1929. for (i = 0; i < NumberOfPtes; i += 1) {
  1930. //
  1931. // Set prototype PTEs.
  1932. //
  1933. if (SectorOffset < EndOfFile.LowPart) {
  1934. //
  1935. // Data resides on the disk, refer to the control section.
  1936. //
  1937. MI_WRITE_INVALID_PTE (PointerPte, TempPte);
  1938. }
  1939. else {
  1940. //
  1941. // Data does not reside on the disk, use Demand zero pages.
  1942. //
  1943. MI_WRITE_INVALID_PTE (PointerPte, TempPteDemandZero);
  1944. }
  1945. SectorOffset += PAGE_SIZE;
  1946. PointerPte += 1;
  1947. }
  1948. }
  1949. else {
  1950. //
  1951. // Set all the prototype PTEs to refer to the control section.
  1952. //
  1953. MiFillMemoryPte (PointerPte, NumberOfPtes, TempPte.u.Long);
  1954. PointerPte += NumberOfPtes;
  1955. }
  1956. NewSegment->u1.ImageCommitment = NumberOfPtes;
  1957. }
  1958. else {
  1959. //
  1960. // Alignment is PAGE_SIZE or greater (or the image is a 32-bit
  1961. // 4K aligned image being run with ALTPTE support).
  1962. //
  1963. if (Subsection->PtesInSubsection > NumberOfPtes) {
  1964. //
  1965. // Inconsistent image, size does not agree with header.
  1966. //
  1967. Status = STATUS_INVALID_IMAGE_FORMAT;
  1968. MI_BAD_IMAGE (0x11);
  1969. goto BadPeImageSegment;
  1970. }
  1971. NumberOfPtes -= Subsection->PtesInSubsection;
  1972. Subsection->NumberOfFullSectors = SizeOfHeaders >> MMSECTOR_SHIFT;
  1973. Subsection->u.SubsectionFlags.SectorEndOffset =
  1974. SizeOfHeaders & MMSECTOR_MASK;
  1975. Subsection->u.SubsectionFlags.ReadOnly = 1;
  1976. Subsection->u.SubsectionFlags.Protection = MM_READONLY;
  1977. TempPte.u.Soft.Protection = MM_READONLY;
  1978. NewSegment->SegmentPteTemplate = TempPte;
  1979. for (i = 0; i < Subsection->PtesInSubsection; i += 1) {
  1980. //
  1981. // Set all the prototype PTEs to refer to the control section.
  1982. //
  1983. if (SectorOffset < SizeOfHeaders) {
  1984. MI_WRITE_INVALID_PTE (PointerPte, TempPte);
  1985. }
  1986. else {
  1987. MI_WRITE_INVALID_PTE (PointerPte, ZeroPte);
  1988. }
  1989. SectorOffset += PAGE_SIZE;
  1990. PointerPte += 1;
  1991. }
  1992. if (CheckSplitPages == TRUE) {
  1993. #if defined (_MIALT4K_)
  1994. NextVa += (MI_ROUND_TO_SIZE (SizeOfHeaders, PAGE_4K));
  1995. #endif
  1996. }
  1997. else {
  1998. NextVa += (i * PAGE_SIZE);
  1999. }
  2000. }
  2001. //
  2002. // Build the additional subsections.
  2003. //
  2004. PreferredImageBase = ImageBase;
  2005. //
  2006. // At this point the object table is read in (if it was not
  2007. // already read in) and may displace the image header.
  2008. //
  2009. SectionTableEntry = NULL;
  2010. OffsetToSectionTable = sizeof(ULONG) +
  2011. sizeof(IMAGE_FILE_HEADER) +
  2012. FileHeader->SizeOfOptionalHeader;
  2013. if ((BYTE_OFFSET(NtHeader) + OffsetToSectionTable +
  2014. #if defined (_WIN64)
  2015. BYTE_OFFSET(NtHeader32) +
  2016. #endif
  2017. ((NumberOfSubsections + 1) *
  2018. sizeof (IMAGE_SECTION_HEADER))) <= PAGE_SIZE) {
  2019. //
  2020. // Section tables are within the header which was read.
  2021. //
  2022. #if defined(_WIN64)
  2023. if (NtHeader32) {
  2024. SectionTableEntry = (PIMAGE_SECTION_HEADER)((PCHAR)NtHeader32 +
  2025. OffsetToSectionTable);
  2026. }
  2027. else
  2028. #endif
  2029. {
  2030. SectionTableEntry = (PIMAGE_SECTION_HEADER)((PCHAR)NtHeader +
  2031. OffsetToSectionTable);
  2032. }
  2033. }
  2034. else {
  2035. //
  2036. // Has an extended header been read in and are the object
  2037. // tables resident?
  2038. //
  2039. if (ExtendedHeader != NULL) {
  2040. #if defined(_WIN64)
  2041. if (NtHeader32) {
  2042. SectionTableEntry = (PIMAGE_SECTION_HEADER)((PCHAR)NtHeader32 +
  2043. OffsetToSectionTable);
  2044. }
  2045. else
  2046. #endif
  2047. {
  2048. SectionTableEntry = (PIMAGE_SECTION_HEADER)((PCHAR)NtHeader +
  2049. OffsetToSectionTable);
  2050. }
  2051. //
  2052. // Is the whole range of object tables mapped by the
  2053. // extended header?
  2054. //
  2055. if ((((PCHAR)SectionTableEntry +
  2056. ((NumberOfSubsections + 1) *
  2057. sizeof (IMAGE_SECTION_HEADER))) -
  2058. (PCHAR)ExtendedHeader) >
  2059. MM_MAXIMUM_IMAGE_HEADER) {
  2060. SectionTableEntry = NULL;
  2061. }
  2062. }
  2063. }
  2064. if (SectionTableEntry == NULL) {
  2065. //
  2066. // The section table entries are not in the same
  2067. // pages as the other data already read in. Read in
  2068. // the object table entries.
  2069. //
  2070. if (ExtendedHeader == NULL) {
  2071. ExtendedHeader = ExAllocatePoolWithTag (NonPagedPool,
  2072. MM_MAXIMUM_IMAGE_HEADER,
  2073. MMTEMPORARY);
  2074. if (ExtendedHeader == NULL) {
  2075. Status = STATUS_INSUFFICIENT_RESOURCES;
  2076. MI_BAD_IMAGE (0x12);
  2077. goto BadPeImageSegment;
  2078. }
  2079. //
  2080. // Build an MDL for the operation.
  2081. //
  2082. MmInitializeMdl (Mdl, ExtendedHeader, MM_MAXIMUM_IMAGE_HEADER);
  2083. MmBuildMdlForNonPagedPool (Mdl);
  2084. }
  2085. StartingOffset.LowPart = PtrToUlong(PAGE_ALIGN (
  2086. (ULONG)DosHeader->e_lfanew +
  2087. OffsetToSectionTable));
  2088. SectionTableEntry = (PIMAGE_SECTION_HEADER)((PCHAR)ExtendedHeader +
  2089. BYTE_OFFSET((ULONG)DosHeader->e_lfanew +
  2090. OffsetToSectionTable));
  2091. KeClearEvent (&InPageEvent);
  2092. Status = IoPageRead (File,
  2093. Mdl,
  2094. &StartingOffset,
  2095. &InPageEvent,
  2096. &IoStatus);
  2097. if (Status == STATUS_PENDING) {
  2098. KeWaitForSingleObject (&InPageEvent,
  2099. WrPageIn,
  2100. KernelMode,
  2101. FALSE,
  2102. (PLARGE_INTEGER)NULL);
  2103. Status = IoStatus.Status;
  2104. }
  2105. if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) {
  2106. MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
  2107. }
  2108. if (!NT_SUCCESS(Status)) {
  2109. MI_BAD_IMAGE (0x13);
  2110. if ((Status != STATUS_FILE_LOCK_CONFLICT) && (Status != STATUS_FILE_IS_OFFLINE)) {
  2111. Status = STATUS_INVALID_FILE_FOR_SECTION;
  2112. }
  2113. goto BadPeImageSegment;
  2114. }
  2115. //
  2116. // From this point on NtHeader is only valid if it
  2117. // was in the first page of the image, otherwise reading in
  2118. // the object tables wiped it out.
  2119. //
  2120. }
  2121. if ((SingleSubsection == TRUE) && (InvalidAlignmentAllowed == FALSE)) {
  2122. //
  2123. // The image header is no longer valid.
  2124. //
  2125. // Loop through all sections and make sure there is no
  2126. // uninitialized data.
  2127. //
  2128. Status = STATUS_SUCCESS;
  2129. while (NumberOfSubsections > 0) {
  2130. if (SectionTableEntry->Misc.VirtualSize == 0) {
  2131. SectionVirtualSize = SectionTableEntry->SizeOfRawData;
  2132. }
  2133. else {
  2134. SectionVirtualSize = SectionTableEntry->Misc.VirtualSize;
  2135. }
  2136. //
  2137. // If the raw pointer + raw size overflows a long word,
  2138. // return an error.
  2139. //
  2140. if (SectionTableEntry->PointerToRawData +
  2141. SectionTableEntry->SizeOfRawData <
  2142. SectionTableEntry->PointerToRawData) {
  2143. KdPrint(("MMCREASECT: invalid section/file size %Z\n",
  2144. &File->FileName));
  2145. Status = STATUS_INVALID_IMAGE_FORMAT;
  2146. MI_BAD_IMAGE (0x14);
  2147. break;
  2148. }
  2149. //
  2150. // If the virtual size and address does not match the rawdata
  2151. // and invalid alignments not allowed return an error.
  2152. //
  2153. if (((SectionTableEntry->PointerToRawData !=
  2154. SectionTableEntry->VirtualAddress))
  2155. ||
  2156. (SectionVirtualSize > SectionTableEntry->SizeOfRawData)) {
  2157. KdPrint(("MMCREASECT: invalid BSS/Trailingzero %Z\n",
  2158. &File->FileName));
  2159. Status = STATUS_INVALID_IMAGE_FORMAT;
  2160. MI_BAD_IMAGE (0x15);
  2161. break;
  2162. }
  2163. SectionTableEntry += 1;
  2164. NumberOfSubsections -= 1;
  2165. }
  2166. if (!NT_SUCCESS(Status)) {
  2167. goto BadPeImageSegment;
  2168. }
  2169. goto PeReturnSuccess;
  2170. }
  2171. if ((SingleSubsection == TRUE) && (InvalidAlignmentAllowed == TRUE)) {
  2172. TempNumberOfSubsections = NumberOfSubsections;
  2173. TempSectionTableEntry = SectionTableEntry;
  2174. //
  2175. // The image header is no longer valid.
  2176. //
  2177. // Loop through all sections and make sure there is no
  2178. // uninitialized data.
  2179. //
  2180. // Also determine if there are shared data sections and
  2181. // the number of extra PTEs they require.
  2182. //
  2183. AdditionalSubsections = 0;
  2184. AdditionalPtes = 0;
  2185. AdditionalBasePtes = 0;
  2186. RoundingAlignment = PAGE_SIZE;
  2187. while (TempNumberOfSubsections > 0) {
  2188. ULONG EndOfSection;
  2189. ULONG ExtraPages;
  2190. if (TempSectionTableEntry->Misc.VirtualSize == 0) {
  2191. SectionVirtualSize = TempSectionTableEntry->SizeOfRawData;
  2192. }
  2193. else {
  2194. SectionVirtualSize = TempSectionTableEntry->Misc.VirtualSize;
  2195. }
  2196. EndOfSection = TempSectionTableEntry->PointerToRawData +
  2197. TempSectionTableEntry->SizeOfRawData;
  2198. //
  2199. // If the raw pointer + raw size overflows a long word, return an error.
  2200. //
  2201. if (EndOfSection < TempSectionTableEntry->PointerToRawData) {
  2202. KdPrint(("MMCREASECT: invalid section/file size %Z\n",
  2203. &File->FileName));
  2204. Status = STATUS_INVALID_IMAGE_FORMAT;
  2205. MI_BAD_IMAGE (0x16);
  2206. goto BadPeImageSegment;
  2207. }
  2208. //
  2209. // If the section goes past SizeOfImage then allocate
  2210. // additional PTEs. On x86 this is handled by the subsection
  2211. // mapping. Note the additional data must be in memory so
  2212. // it can be shuffled around later.
  2213. //
  2214. if ((EndOfSection <= EndOfFile.LowPart) &&
  2215. (EndOfSection > SizeOfImage)) {
  2216. //
  2217. // Allocate enough PTEs to cover the end of this section.
  2218. // Maximize with any other sections that extend beyond SizeOfImage
  2219. //
  2220. ExtraPages = MI_ROUND_TO_SIZE (EndOfSection, RoundingAlignment) >> PAGE_SHIFT;
  2221. if ((ExtraPages > OrigNumberOfPtes) &&
  2222. (ExtraPages - OrigNumberOfPtes > AdditionalBasePtes)) {
  2223. AdditionalBasePtes = ExtraPages - (ULONG) OrigNumberOfPtes;
  2224. }
  2225. }
  2226. //
  2227. // Count number of shared data sections and additional PTEs needed.
  2228. //
  2229. if ((TempSectionTableEntry->Characteristics & IMAGE_SCN_MEM_SHARED) &&
  2230. (!(TempSectionTableEntry->Characteristics & IMAGE_SCN_MEM_EXECUTE) ||
  2231. (TempSectionTableEntry->Characteristics & IMAGE_SCN_MEM_WRITE))) {
  2232. AdditionalPtes +=
  2233. MI_ROUND_TO_SIZE (SectionVirtualSize, RoundingAlignment) >>
  2234. PAGE_SHIFT;
  2235. AdditionalSubsections += 1;
  2236. }
  2237. TempSectionTableEntry += 1;
  2238. TempNumberOfSubsections -= 1;
  2239. }
  2240. if (AdditionalBasePtes == 0 && (AdditionalSubsections == 0 || AdditionalPtes == 0)) {
  2241. //
  2242. // No shared data sections.
  2243. //
  2244. goto PeReturnSuccess;
  2245. }
  2246. //
  2247. // There are additional base PTEs or shared data sections.
  2248. // For shared sections, allocate new PTEs for these sections
  2249. // at the end of the image. The usermode wow loader will change
  2250. // fixups to point to the new pages.
  2251. //
  2252. // First reallocate the control area.
  2253. //
  2254. NewSubsectionsAllocated = SubsectionsAllocated + AdditionalSubsections;
  2255. NewControlArea = ExAllocatePoolWithTag(NonPagedPool,
  2256. (ULONG) (sizeof(CONTROL_AREA) +
  2257. (sizeof(SUBSECTION) *
  2258. NewSubsectionsAllocated)),
  2259. 'iCmM');
  2260. if (NewControlArea == NULL) {
  2261. MI_BAD_IMAGE (0x17);
  2262. Status = STATUS_INSUFFICIENT_RESOURCES;
  2263. goto BadPeImageSegment;
  2264. }
  2265. //
  2266. // Copy the old control area to the new one, modify some fields.
  2267. //
  2268. RtlCopyMemory (NewControlArea, ControlArea,
  2269. sizeof(CONTROL_AREA) +
  2270. sizeof(SUBSECTION) * SubsectionsAllocated);
  2271. //
  2272. // Now allocate a new segment that has the newly calculated number
  2273. // of PTEs, initialize it from the previously allocated new segment,
  2274. // and overwrite the fields that should be changed.
  2275. //
  2276. OldSegment = NewSegment;
  2277. OrigNumberOfPtes += AdditionalBasePtes;
  2278. PointerPte += AdditionalBasePtes;
  2279. SizeOfSegment = sizeof(SEGMENT) +
  2280. (sizeof(MMPTE) * (OrigNumberOfPtes + AdditionalPtes - 1)) +
  2281. sizeof(SECTION_IMAGE_INFORMATION);
  2282. NewSegment = ExAllocatePoolWithTag (PagedPool | POOL_MM_ALLOCATION,
  2283. SizeOfSegment,
  2284. MMSECT);
  2285. if (NewSegment == NULL) {
  2286. //
  2287. // The requested pool could not be allocated.
  2288. //
  2289. MI_BAD_IMAGE (0x18);
  2290. ExFreePool (NewControlArea);
  2291. ExFreePool (OldSegment);
  2292. Status = STATUS_INSUFFICIENT_RESOURCES;
  2293. goto BadPeImageSegment;
  2294. }
  2295. *Segment = NewSegment;
  2296. RtlCopyMemory (NewSegment, OldSegment, sizeof(SEGMENT));
  2297. //
  2298. // Align the prototype PTEs on the proper boundary.
  2299. //
  2300. NewPointerPte = &NewSegment->ThePtes[0];
  2301. NewSegment->PrototypePte = &NewSegment->ThePtes[0];
  2302. PointerPte = NewSegment->PrototypePte +
  2303. (PointerPte - OldSegment->PrototypePte);
  2304. NewSegment->ControlArea = NewControlArea;
  2305. NewSegment->SegmentFlags.ExtraSharedWowSubsections = 1;
  2306. NewSegment->u2.ImageInformation =
  2307. (PSECTION_IMAGE_INFORMATION)((PCHAR)NewSegment + sizeof(SEGMENT) +
  2308. (sizeof(MMPTE) * (OrigNumberOfPtes + AdditionalPtes - 1)));
  2309. NewSegment->TotalNumberOfPtes = (ULONG)(OrigNumberOfPtes + AdditionalPtes);
  2310. NewSegment->NonExtendedPtes = (ULONG)(OrigNumberOfPtes + AdditionalPtes);
  2311. NewSegment->SizeOfSegment = (UINT64)(OrigNumberOfPtes + AdditionalPtes) * PAGE_SIZE;
  2312. RtlCopyMemory (NewSegment->u2.ImageInformation,
  2313. OldSegment->u2.ImageInformation,
  2314. sizeof (SECTION_IMAGE_INFORMATION));
  2315. //
  2316. // Adjust the prototype PTE pointer of the PE header page to point
  2317. // at the new segment.
  2318. //
  2319. ASSERT (Pfn1->u2.Blink == (PFN_NUMBER) OldSegment->PrototypePte);
  2320. Pfn1->u2.Blink = (PFN_NUMBER) NewSegment->PrototypePte;
  2321. //
  2322. // Now change the fields in the subsections to account for the new
  2323. // control area and the new segment. Also change the PTEs in the
  2324. // newly allocated segment to point to the new subsections.
  2325. //
  2326. NewControlArea->Segment = NewSegment;
  2327. Subsection = (PSUBSECTION)(ControlArea + 1);
  2328. NewSubsection = (PSUBSECTION)(NewControlArea + 1);
  2329. NewSubsection->PtesInSubsection += AdditionalBasePtes;
  2330. for (i = 0; i < SubsectionsAllocated; i += 1) {
  2331. //
  2332. // Note: SubsectionsAllocated is always 1 (for wx86), so this loop
  2333. // is executed only once.
  2334. //
  2335. NewSubsection->ControlArea = (PCONTROL_AREA) NewControlArea;
  2336. NewSubsection->SubsectionBase = NewSegment->PrototypePte +
  2337. (Subsection->SubsectionBase - OldSegment->PrototypePte);
  2338. NewPointerPte = NewSegment->PrototypePte;
  2339. OldPointerPte = OldSegment->PrototypePte;
  2340. TempPte.u.Long = MiGetSubsectionAddressForPte (NewSubsection);
  2341. TempPte.u.Soft.Prototype = 1;
  2342. for (j = 0; j < OldSegment->TotalNumberOfPtes+AdditionalBasePtes; j += 1) {
  2343. if ((OldPointerPte->u.Soft.Prototype == 1) &&
  2344. (MiGetSubsectionAddress (OldPointerPte) == Subsection)) {
  2345. OriginalProtection = MI_GET_PROTECTION_FROM_SOFT_PTE (OldPointerPte);
  2346. TempPte.u.Soft.Protection = OriginalProtection;
  2347. MI_WRITE_INVALID_PTE (NewPointerPte, TempPte);
  2348. }
  2349. else if (i == 0) {
  2350. //
  2351. // Since the outer for loop is executed only once, there
  2352. // is no need for the i == 0 above, but it is safer to
  2353. // have it. If the code changes later and other sections
  2354. // are added, their PTEs will get initialized here as
  2355. // DemandZero and if they are not DemandZero, they will be
  2356. // overwritten in a later iteration of the outer loop.
  2357. // For now, this else if clause will be executed only
  2358. // for DemandZero PTEs.
  2359. //
  2360. OriginalProtection = MI_GET_PROTECTION_FROM_SOFT_PTE (OldPointerPte);
  2361. TempPteDemandZero.u.Long = 0;
  2362. TempPteDemandZero.u.Soft.Protection = OriginalProtection;
  2363. MI_WRITE_INVALID_PTE (NewPointerPte, TempPteDemandZero);
  2364. }
  2365. NewPointerPte += 1;
  2366. //
  2367. // Stop incrementing the OldPointerPte at the last entry
  2368. // and use it for the additional base PTEs.
  2369. //
  2370. if (j < OldSegment->TotalNumberOfPtes - 1) {
  2371. OldPointerPte += 1;
  2372. }
  2373. }
  2374. Subsection += 1;
  2375. NewSubsection += 1;
  2376. }
  2377. RtlZeroMemory (NewSubsection,
  2378. sizeof(SUBSECTION) * AdditionalSubsections);
  2379. ExFreePool (OldSegment);
  2380. ExFreePool (ControlArea);
  2381. ControlArea = (PCONTROL_AREA) NewControlArea;
  2382. //
  2383. // Adjust some variables that are used below.
  2384. // PointerPte has already been set above before OldSegment was freed.
  2385. //
  2386. SubsectionsAllocated = NewSubsectionsAllocated;
  2387. Subsection = NewSubsection - 1; // Points to last used subsection.
  2388. NumberOfPtes = AdditionalPtes; // # PTEs that haven't yet been used in
  2389. // previous subsections.
  2390. //
  2391. // Additional base PTEs have been added. Only continue if there are
  2392. // additional subsections to process.
  2393. //
  2394. if (AdditionalSubsections == 0 || AdditionalPtes == 0) {
  2395. // no shared data sections
  2396. goto PeReturnSuccess;
  2397. }
  2398. }
  2399. ImageFileSize = EndOfFile.LowPart + 1;
  2400. #if defined (_MIALT4K_)
  2401. if (CheckSplitPages == TRUE) {
  2402. RoundingAlignment = PAGE_SIZE;
  2403. }
  2404. PreviousSectionCharacteristics = IMAGE_SCN_MEM_READ;
  2405. #endif
  2406. while (NumberOfSubsections > 0) {
  2407. if ((InvalidAlignmentAllowed == FALSE) ||
  2408. ((SectionTableEntry->Characteristics & IMAGE_SCN_MEM_SHARED) &&
  2409. (!(SectionTableEntry->Characteristics & IMAGE_SCN_MEM_EXECUTE) ||
  2410. (SectionTableEntry->Characteristics & IMAGE_SCN_MEM_WRITE)))) {
  2411. //
  2412. // Handle case where virtual size is 0.
  2413. //
  2414. if (SectionTableEntry->Misc.VirtualSize == 0) {
  2415. SectionVirtualSize = SectionTableEntry->SizeOfRawData;
  2416. }
  2417. else {
  2418. SectionVirtualSize = SectionTableEntry->Misc.VirtualSize;
  2419. }
  2420. //
  2421. // Fix for Borland linker problem. The SizeOfRawData can
  2422. // be a zero, but the PointerToRawData is not zero.
  2423. // Set it to zero.
  2424. //
  2425. if (SectionTableEntry->SizeOfRawData == 0) {
  2426. SectionTableEntry->PointerToRawData = 0;
  2427. }
  2428. //
  2429. // If the section information wraps return an error.
  2430. //
  2431. if (SectionTableEntry->PointerToRawData +
  2432. SectionTableEntry->SizeOfRawData <
  2433. SectionTableEntry->PointerToRawData) {
  2434. MI_BAD_IMAGE (0x19);
  2435. Status = STATUS_INVALID_IMAGE_FORMAT;
  2436. goto BadPeImageSegment;
  2437. }
  2438. Subsection->NextSubsection = (Subsection + 1);
  2439. Subsection += 1;
  2440. Subsection->ControlArea = ControlArea;
  2441. Subsection->NextSubsection = NULL;
  2442. Subsection->UnusedPtes = 0;
  2443. if (((NextVa != (PreferredImageBase + SectionTableEntry->VirtualAddress)) && (InvalidAlignmentAllowed == FALSE)) ||
  2444. (SectionVirtualSize == 0)) {
  2445. //
  2446. // The specified virtual address does not align
  2447. // with the next prototype PTE.
  2448. //
  2449. MI_BAD_IMAGE (0x1A);
  2450. Status = STATUS_INVALID_IMAGE_FORMAT;
  2451. goto BadPeImageSegment;
  2452. }
  2453. Subsection->PtesInSubsection =
  2454. MI_ROUND_TO_SIZE (SectionVirtualSize, RoundingAlignment)
  2455. >> PAGE_SHIFT;
  2456. #if defined (_MIALT4K_)
  2457. if (CheckSplitPages == TRUE) {
  2458. Subsection->PtesInSubsection =
  2459. (ULONG) MI_COMPUTE_PAGES_SPANNED (NextVa,
  2460. SectionVirtualSize);
  2461. }
  2462. #endif
  2463. if (Subsection->PtesInSubsection > NumberOfPtes) {
  2464. LOGICAL ImageOk;
  2465. ImageOk = FALSE;
  2466. #if defined (_MIALT4K_)
  2467. //
  2468. // If this is a split wow binary then the PtesInSubsection
  2469. // calculation may not have factored in that we may shortly
  2470. // slide (or completely combine) this subsection into
  2471. // the previous one.
  2472. //
  2473. if ((CheckSplitPages == TRUE) &&
  2474. (BYTE_OFFSET (SectionTableEntry->VirtualAddress) != 0)) {
  2475. if (SectionVirtualSize > PAGE_4K) {
  2476. //
  2477. // This subsection will slide below, check it
  2478. // accordingly.
  2479. //
  2480. if ((ULONG) MI_COMPUTE_PAGES_SPANNED (NextVa + PAGE_4K,
  2481. SectionVirtualSize - PAGE_4K) == NumberOfPtes) {
  2482. //
  2483. // The slide will make things right so
  2484. // this image is valid after all. March on.
  2485. // Note this may result in the NumberOfPtes local
  2486. // temporarily going negative, but that's ok as it
  2487. // will get fixed at the time of the slide.
  2488. //
  2489. ImageOk = TRUE;
  2490. }
  2491. }
  2492. else {
  2493. //
  2494. // This subsection will be combined below so we know
  2495. // it's ok to keep going.
  2496. //
  2497. // Note this may result in the NumberOfPtes local
  2498. // temporarily going negative, but that's ok as it
  2499. // will get fixed at the time of the slide.
  2500. //
  2501. ImageOk = TRUE;
  2502. }
  2503. }
  2504. #endif
  2505. if (ImageOk == FALSE) {
  2506. //
  2507. // Inconsistent image, size does not agree with
  2508. // object tables.
  2509. //
  2510. MI_BAD_IMAGE (0x1B);
  2511. Status = STATUS_INVALID_IMAGE_FORMAT;
  2512. goto BadPeImageSegment;
  2513. }
  2514. }
  2515. NumberOfPtes -= Subsection->PtesInSubsection;
  2516. Subsection->u.LongFlags = 0;
  2517. Subsection->StartingSector =
  2518. SectionTableEntry->PointerToRawData >> MMSECTOR_SHIFT;
  2519. //
  2520. // Align ending sector on file align boundary.
  2521. //
  2522. EndingAddress = (SectionTableEntry->PointerToRawData +
  2523. SectionTableEntry->SizeOfRawData +
  2524. FileAlignment) & ~FileAlignment;
  2525. Subsection->NumberOfFullSectors = (ULONG)
  2526. ((EndingAddress >> MMSECTOR_SHIFT) -
  2527. Subsection->StartingSector);
  2528. Subsection->u.SubsectionFlags.SectorEndOffset =
  2529. (ULONG) EndingAddress & MMSECTOR_MASK;
  2530. Subsection->SubsectionBase = PointerPte;
  2531. //
  2532. // Build both a demand zero PTE and a PTE pointing to the
  2533. // subsection.
  2534. //
  2535. TempPte.u.Long = 0;
  2536. TempPteDemandZero.u.Long = 0;
  2537. TempPte.u.Long = MiGetSubsectionAddressForPte (Subsection);
  2538. TempPte.u.Soft.Prototype = 1;
  2539. ImageFileSize = SectionTableEntry->PointerToRawData +
  2540. SectionTableEntry->SizeOfRawData;
  2541. TempPte.u.Soft.Protection =
  2542. MiGetImageProtection (SectionTableEntry->Characteristics);
  2543. TempPteDemandZero.u.Soft.Protection = TempPte.u.Soft.Protection;
  2544. if (SectionTableEntry->PointerToRawData == 0) {
  2545. TempPte = TempPteDemandZero;
  2546. }
  2547. Subsection->u.SubsectionFlags.ReadOnly = 1;
  2548. Subsection->u.SubsectionFlags.Protection = MI_GET_PROTECTION_FROM_SOFT_PTE (&TempPte);
  2549. //
  2550. // Assume the subsection will be unwritable and therefore
  2551. // won't be charged for any commitment.
  2552. //
  2553. SectionCommit = FALSE;
  2554. ImageCommit = FALSE;
  2555. if (TempPte.u.Soft.Protection & MM_PROTECTION_WRITE_MASK) {
  2556. if ((TempPte.u.Soft.Protection & MM_COPY_ON_WRITE_MASK)
  2557. == MM_COPY_ON_WRITE_MASK) {
  2558. //
  2559. // This page is copy on write, charge ImageCommitment
  2560. // for all pages in this subsection.
  2561. //
  2562. ImageCommit = TRUE;
  2563. }
  2564. else {
  2565. //
  2566. // This page is write shared, charge commitment when
  2567. // the mapping completes.
  2568. //
  2569. SectionCommit = TRUE;
  2570. Subsection->u.SubsectionFlags.GlobalMemory = 1;
  2571. ControlArea->u.Flags.GlobalMemory = 1;
  2572. }
  2573. }
  2574. NewSegment->SegmentPteTemplate = TempPte;
  2575. SectorOffset = 0;
  2576. SizeOfRawData = SectionTableEntry->SizeOfRawData;
  2577. #if defined (_MIALT4K_)
  2578. //
  2579. // Check whether split permissions need to be applied to straddlers.
  2580. //
  2581. if ((CheckSplitPages == TRUE) &&
  2582. (BYTE_OFFSET (SectionTableEntry->VirtualAddress) != 0)) {
  2583. ASSERT (BYTE_OFFSET (SectionTableEntry->VirtualAddress) == PAGE_4K);
  2584. PreviousPte = PointerPte - 1;
  2585. ASSERT ((PreviousPte >= NewSegment->PrototypePte) &&
  2586. (PreviousPte < NewSegment->PrototypePte + NewSegment->TotalNumberOfPtes));
  2587. PteContents.u.Long = PreviousPte->u.Long;
  2588. ASSERT (PteContents.u.Hard.Valid == 0);
  2589. //
  2590. // Read in (if it's filesystem backed instead of demand zero or
  2591. // no access) 4K of the previous page. Combine it with the
  2592. // first 4K contents of this page.
  2593. //
  2594. if (PreviousPte == NewSegment->PrototypePte) {
  2595. StraddleFrameNumber = PageFrameNumber;
  2596. PageFrameNumber = 0;
  2597. }
  2598. else {
  2599. StraddleFrameNumber = MiGetPageForHeader (TRUE);
  2600. ASSERT (StraddleFrameNumber != 0);
  2601. Pfn1 = MI_PFN_ELEMENT (StraddleFrameNumber);
  2602. ASSERT (Pfn1->u1.Flink == 0);
  2603. Pfn1->u1.Flink = (PFN_NUMBER) ImagePages;
  2604. Pfn1->u2.Blink = (PFN_NUMBER) PreviousPte;
  2605. ImagePages = Pfn1;
  2606. }
  2607. //
  2608. // Either the current 4k page or the previous 4k page (or both)
  2609. // may need to be read from the filesystem (vs being BSS).
  2610. //
  2611. StraddleVa = MiMapSinglePage (NULL,
  2612. StraddleFrameNumber,
  2613. MmCached,
  2614. HighPagePriority);
  2615. if (StraddleVa == NULL) {
  2616. MI_BAD_IMAGE (0x1C);
  2617. Status = STATUS_INSUFFICIENT_RESOURCES;
  2618. goto BadPeImageSegment;
  2619. }
  2620. //
  2621. // Create an event and MDL for the read operation.
  2622. //
  2623. HalfPage = ExAllocatePoolWithTag (NonPagedPool,
  2624. PAGE_SIZE,
  2625. MMTEMPORARY);
  2626. if (HalfPage == NULL) {
  2627. MiUnmapSinglePage (StraddleVa);
  2628. MI_BAD_IMAGE (0x1D);
  2629. Status = STATUS_INSUFFICIENT_RESOURCES;
  2630. goto BadPeImageSegment;
  2631. }
  2632. HalfPagePhysicalAddress = MmGetPhysicalAddress (HalfPage);
  2633. ASSERT (HalfPagePhysicalAddress.QuadPart != 0);
  2634. HalfPageFrameNumber = (HalfPagePhysicalAddress.QuadPart >> PAGE_SHIFT);
  2635. ReadCount = 0;
  2636. if (PteContents.u.Soft.Prototype == 1) {
  2637. ReadSubsection = Subsection - 1;
  2638. ReadPte = PreviousPte;
  2639. TwoReads:
  2640. IoStatus.Information = 0;
  2641. ASSERT (Mdl == (PMDL) &StackMdl);
  2642. KeInitializeEvent (&InPageEvent, NotificationEvent, FALSE);
  2643. MmInitializeMdl (Mdl, NULL, PAGE_4K);
  2644. Mdl->MdlFlags |= MDL_PAGES_LOCKED;
  2645. Page = (PPFN_NUMBER)(Mdl + 1);
  2646. *Page = HalfPageFrameNumber;
  2647. StartingOffset.QuadPart = MI_STARTING_OFFSET (ReadSubsection,
  2648. ReadPte);
  2649. TempOffset = MiEndingOffset (ReadSubsection);
  2650. ASSERT (StartingOffset.QuadPart < TempOffset.QuadPart);
  2651. if (((UINT64)StartingOffset.QuadPart + PAGE_4K) > (UINT64)TempOffset.QuadPart) {
  2652. ASSERT ((ULONG)(TempOffset.QuadPart - StartingOffset.QuadPart)
  2653. > 0);
  2654. ReadSize = (ULONG)(TempOffset.QuadPart - StartingOffset.QuadPart);
  2655. //
  2656. // Round the offset to a 512-byte offset as this
  2657. // will help filesystems optimize the transfer.
  2658. // Note that filesystems will always zero fill
  2659. // the remainder between VDL and the next 512-byte
  2660. // multiple and we have already zeroed the whole page.
  2661. //
  2662. ReadSize = ((ReadSize + MMSECTOR_MASK) & ~MMSECTOR_MASK);
  2663. Mdl->ByteCount = ReadSize;
  2664. }
  2665. Status = IoPageRead (File,
  2666. Mdl,
  2667. &StartingOffset,
  2668. &InPageEvent,
  2669. &IoStatus);
  2670. if (Status == STATUS_PENDING) {
  2671. KeWaitForSingleObject (&InPageEvent,
  2672. WrPageIn,
  2673. KernelMode,
  2674. FALSE,
  2675. NULL);
  2676. Status = IoStatus.Status;
  2677. }
  2678. if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) {
  2679. MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
  2680. }
  2681. if (!NT_SUCCESS (Status)) {
  2682. MI_BAD_IMAGE (0x1E);
  2683. ExFreePool (HalfPage);
  2684. MiUnmapSinglePage (StraddleVa);
  2685. goto BadPeImageSegment;
  2686. }
  2687. //
  2688. // Less than a full page may be read from the file,
  2689. // only copy the amount that was read (the rest of
  2690. // the target page is zero already).
  2691. //
  2692. ASSERT (IoStatus.Information <= PAGE_4K);
  2693. if ((ReadCount == 0) &&
  2694. (PreviousPte == NewSegment->PrototypePte)) {
  2695. ASSERT (PageFrameNumber == 0);
  2696. }
  2697. else {
  2698. RtlCopyMemory ((PVOID) ((PCHAR) StraddleVa + ReadCount * PAGE_4K),
  2699. HalfPage,
  2700. IoStatus.Information);
  2701. }
  2702. }
  2703. ReadCount += 1;
  2704. //
  2705. // Now fill the second 4K of this native page. Either the
  2706. // existing zero fill is sufficient or it must be
  2707. // retrieved from the filesystem.
  2708. //
  2709. if ((ReadCount == 1) && (SectionVirtualSize != 0)) {
  2710. if ((SectionTableEntry->SizeOfRawData != 0) &&
  2711. (SectionTableEntry->PointerToRawData != 0)) {
  2712. //
  2713. // Data must be retrieved from the filesystem.
  2714. //
  2715. ReadSubsection = Subsection;
  2716. ReadPte = PointerPte;
  2717. goto TwoReads;
  2718. }
  2719. else {
  2720. RtlZeroMemory ((PVOID) ((PCHAR) StraddleVa + PAGE_4K),
  2721. PAGE_4K);
  2722. }
  2723. }
  2724. if ((ReadCount == 2) &&
  2725. (SectionTableEntry->SizeOfRawData < PAGE_4K)) {
  2726. //
  2727. // The second 4K of this native page contains a
  2728. // mixture of initialized data and BSS. We already
  2729. // read the whole 4K so zero any BSS now.
  2730. //
  2731. RawDataSize = SectionTableEntry->SizeOfRawData;
  2732. RawDataSize &= ~(sizeof (ULONGLONG) - 1);
  2733. RtlZeroMemory ((PVOID) ((PCHAR) StraddleVa + PAGE_4K +
  2734. RawDataSize),
  2735. PAGE_4K - RawDataSize);
  2736. }
  2737. MiUnmapSinglePage (StraddleVa);
  2738. ExFreePool (HalfPage);
  2739. //
  2740. // Update the last prototype PTE of the previous subsection
  2741. // to indicate the permissions may need to be split. This
  2742. // ensures the last PTE of the image header (for example)
  2743. // gets marked correctly.
  2744. //
  2745. PteContents.u.Soft.SplitPermissions = 1;
  2746. //
  2747. // Store the protection for the last half of the page in
  2748. // the subsection itself. Later when the image is mapped we
  2749. // must retrieve it from here - you would think you could just
  2750. // look forward to the next subsection and get it from there -
  2751. // but you can't for the scenario where we merge the last
  2752. // subsection into the prior one and thus would have no
  2753. // place to get the protection for the last half of the split
  2754. // page in this case.
  2755. //
  2756. (Subsection - 1)->LastSplitPageProtection = Subsection->u.SubsectionFlags.Protection;
  2757. //
  2758. // The native PTE gets the most relaxed permissions of the
  2759. // 2 split pages. Each alternate PTE gets the exact permission
  2760. // granted as the alternate PTE permission always overrides
  2761. // that of the native PTE.
  2762. //
  2763. ASSERT ((PreviousSectionCharacteristics & IMAGE_SCN_MEM_SHARED) == 0);
  2764. ASSERT ((SectionTableEntry->Characteristics & IMAGE_SCN_MEM_SHARED) == 0);
  2765. PteContents.u.Soft.Protection =
  2766. MiGetImageProtection (SectionTableEntry->Characteristics |
  2767. PreviousSectionCharacteristics);
  2768. MI_WRITE_INVALID_PTE (PreviousPte, PteContents);
  2769. //
  2770. // Start the prototype PTEs for the current subsection
  2771. // so they don't include any starting split portion because
  2772. // that must always be tacked onto the end of the previous
  2773. // subsection to ensure that there is only a single
  2774. // prototype PTE for any given physical page. This
  2775. // may result in no prototype PTE at all for this subsection
  2776. // if the virtual size for it doesn't cross over into an
  2777. // additional page.
  2778. //
  2779. NumberOfPtes += 1;
  2780. NextVa += PAGE_4K;
  2781. if (SectionVirtualSize > PAGE_4K) {
  2782. //
  2783. // Slide the current subsection forward to account
  2784. // for the beginning that was "moved" into the previous
  2785. // subsection.
  2786. //
  2787. SectorOffset = (Subsection->NumberOfFullSectors << MMSECTOR_SHIFT);
  2788. if (Subsection->u.SubsectionFlags.SectorEndOffset != 0) {
  2789. SectorOffset += MMSECTOR_SHIFT;
  2790. }
  2791. if (SectorOffset > PAGE_4K) {
  2792. SectorOffset = PAGE_4K;
  2793. Subsection->StartingSector += (SectorOffset >> MMSECTOR_SHIFT);
  2794. Subsection->NumberOfFullSectors -= (SectorOffset >> MMSECTOR_SHIFT);
  2795. }
  2796. else if (Subsection->u.SubsectionFlags.SectorEndOffset != 0) {
  2797. Subsection->StartingSector += (SectorOffset >> MMSECTOR_SHIFT);
  2798. Subsection->u.SubsectionFlags.SectorEndOffset = 0;
  2799. Subsection->NumberOfFullSectors -= ((SectorOffset - 1) >> MMSECTOR_SHIFT);
  2800. }
  2801. Subsection->PtesInSubsection = (ULONG)
  2802. MI_COMPUTE_PAGES_SPANNED (NextVa,
  2803. SectionVirtualSize - PAGE_4K);
  2804. //
  2805. // Update sector offset and size of raw data to contain
  2806. // the alignment of the start of this subsection and its
  2807. // new size so BSS calculations below will be correct.
  2808. // ie - we always align each subsection at zero (pushing
  2809. // its first 4K page into the previous subsection if
  2810. // necessary).
  2811. //
  2812. SectorOffset = 0;
  2813. if (SizeOfRawData >= PAGE_4K) {
  2814. SizeOfRawData -= PAGE_4K;
  2815. }
  2816. else {
  2817. SizeOfRawData = 0;
  2818. }
  2819. }
  2820. else {
  2821. //
  2822. // This straddling subsection fits entirely in the
  2823. // previous subsection so there is no additional
  2824. // subsection to use for it - eliminate the current
  2825. // subsection now.
  2826. //
  2827. RtlZeroMemory (Subsection, sizeof (SUBSECTION));
  2828. Subsection -= 1;
  2829. Subsection->NextSubsection = NULL;
  2830. PreviousSectionCharacteristics = SectionTableEntry->Characteristics;
  2831. SectionTableEntry += 1;
  2832. NumberOfSubsections -= 1;
  2833. //
  2834. // Reduce the allocated subsection count as we have merged
  2835. // this subsection. This is critical if there are any
  2836. // shared subsections in the image because we use this
  2837. // count to decide how much to allocate later as well as
  2838. // how many subsections will get non-NULL NextSubsection
  2839. // fields - so if it is too high we will end up with
  2840. // corrupt subsections on the end of the control area.
  2841. //
  2842. SubsectionsAllocated -= 1;
  2843. continue;
  2844. }
  2845. }
  2846. PreviousSectionCharacteristics = SectionTableEntry->Characteristics;
  2847. #endif
  2848. for (i = 0; i < Subsection->PtesInSubsection; i += 1) {
  2849. //
  2850. // Set all the prototype PTEs to refer to the control section.
  2851. //
  2852. #if defined (_MIALT4K_)
  2853. //
  2854. // We are native page aligned on entry here because we
  2855. // adjusted non-native alignment above. However, within a
  2856. // single subsection there can be both data and BSS/noaccess
  2857. // ranges which can occur on a 4k boundary. Thus, this must
  2858. // be explicitly checked for and handled here.
  2859. //
  2860. #endif
  2861. if (SectorOffset < SectionVirtualSize) {
  2862. //
  2863. // Make PTE accessible.
  2864. //
  2865. if (SectionCommit) {
  2866. NewSegment->NumberOfCommittedPages += 1;
  2867. }
  2868. if (ImageCommit) {
  2869. NewSegment->u1.ImageCommitment += 1;
  2870. }
  2871. if (SectorOffset < SizeOfRawData) {
  2872. //
  2873. // Data resides on the disk, use the subsection
  2874. // format PTE.
  2875. //
  2876. MI_WRITE_INVALID_PTE (PointerPte, TempPte);
  2877. }
  2878. else {
  2879. //
  2880. // Demand zero pages.
  2881. //
  2882. MI_WRITE_INVALID_PTE (PointerPte, TempPteDemandZero);
  2883. #if defined (_MIALT4K_)
  2884. //
  2885. // Check the previous PTE for straddling.
  2886. //
  2887. if ((CheckSplitPages == TRUE) &&
  2888. (i != 0) &&
  2889. (SectorOffset - PAGE_SIZE < SizeOfRawData) &&
  2890. (BYTE_OFFSET (SizeOfRawData) != 0) &&
  2891. (BYTE_OFFSET (SizeOfRawData) <= PAGE_4K)) {
  2892. //
  2893. // The last half of the previous PTE is actually
  2894. // the straddler - ie: the previous PTE's first half
  2895. // needs to be filled from the filesystem and the
  2896. // second half must be zero-filled.
  2897. //
  2898. // Note the previous PTE does NOT need to be marked
  2899. // for split permissions because the permissions
  2900. // are actually the same. The page is only
  2901. // materialized so it is filled properly and this
  2902. // only needs to happen this one time.
  2903. //
  2904. PreviousPte = PointerPte - 1;
  2905. ASSERT ((PreviousPte >= NewSegment->PrototypePte) &&
  2906. (PreviousPte < NewSegment->PrototypePte + NewSegment->TotalNumberOfPtes));
  2907. PteContents.u.Long = PreviousPte->u.Long;
  2908. ASSERT (PteContents.u.Hard.Valid == 0);
  2909. ASSERT (PteContents.u.Soft.Prototype == 1);
  2910. StraddleFrameNumber = MiGetPageForHeader (TRUE);
  2911. ASSERT (StraddleFrameNumber != 0);
  2912. Pfn1 = MI_PFN_ELEMENT (StraddleFrameNumber);
  2913. ASSERT (Pfn1->u1.Flink == 0);
  2914. Pfn1->u1.Flink = (PFN_NUMBER) ImagePages;
  2915. Pfn1->u2.Blink = (PFN_NUMBER) PreviousPte;
  2916. ImagePages = Pfn1;
  2917. StraddleVa = MiMapSinglePage (NULL,
  2918. StraddleFrameNumber,
  2919. MmCached,
  2920. HighPagePriority);
  2921. if (StraddleVa == NULL) {
  2922. MI_BAD_IMAGE (0x1F);
  2923. Status = STATUS_INSUFFICIENT_RESOURCES;
  2924. goto BadPeImageSegment;
  2925. }
  2926. //
  2927. // Create an event and MDL for the read operation.
  2928. //
  2929. HalfPage = ExAllocatePoolWithTag (NonPagedPool,
  2930. PAGE_SIZE,
  2931. MMTEMPORARY);
  2932. if (HalfPage == NULL) {
  2933. MiUnmapSinglePage (StraddleVa);
  2934. goto BadPeImageSegment;
  2935. }
  2936. HalfPagePhysicalAddress = MmGetPhysicalAddress (HalfPage);
  2937. ASSERT (HalfPagePhysicalAddress.QuadPart != 0);
  2938. HalfPageFrameNumber = (HalfPagePhysicalAddress.QuadPart >> PAGE_SHIFT);
  2939. IoStatus.Information = 0;
  2940. ASSERT (Mdl == (PMDL) &StackMdl);
  2941. KeInitializeEvent (&InPageEvent, NotificationEvent, FALSE);
  2942. MmInitializeMdl (Mdl, NULL, PAGE_4K);
  2943. Mdl->MdlFlags |= MDL_PAGES_LOCKED;
  2944. Page = (PPFN_NUMBER)(Mdl + 1);
  2945. *Page = HalfPageFrameNumber;
  2946. StartingOffset.QuadPart = MI_STARTING_OFFSET (Subsection,
  2947. PreviousPte);
  2948. TempOffset = MiEndingOffset (Subsection);
  2949. ASSERT (StartingOffset.QuadPart < TempOffset.QuadPart);
  2950. if (((UINT64)StartingOffset.QuadPart + PAGE_4K) > (UINT64)TempOffset.QuadPart) {
  2951. ASSERT ((ULONG)(TempOffset.QuadPart - StartingOffset.QuadPart)
  2952. > 0);
  2953. ReadSize = (ULONG)(TempOffset.QuadPart - StartingOffset.QuadPart);
  2954. //
  2955. // Round the offset to a 512-byte offset as this
  2956. // will help filesystems optimize the transfer.
  2957. // Note that filesystems will always zero fill
  2958. // the remainder between VDL and the next 512-byte
  2959. // multiple and we have already zeroed the whole page.
  2960. //
  2961. ReadSize = ((ReadSize + MMSECTOR_MASK) & ~MMSECTOR_MASK);
  2962. Mdl->ByteCount = ReadSize;
  2963. }
  2964. Status = IoPageRead (File,
  2965. Mdl,
  2966. &StartingOffset,
  2967. &InPageEvent,
  2968. &IoStatus);
  2969. if (Status == STATUS_PENDING) {
  2970. KeWaitForSingleObject (&InPageEvent,
  2971. WrPageIn,
  2972. KernelMode,
  2973. FALSE,
  2974. NULL);
  2975. Status = IoStatus.Status;
  2976. }
  2977. if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) {
  2978. MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
  2979. }
  2980. if (!NT_SUCCESS (Status)) {
  2981. MI_BAD_IMAGE (0x20);
  2982. ExFreePool (HalfPage);
  2983. MiUnmapSinglePage (StraddleVa);
  2984. goto BadPeImageSegment;
  2985. }
  2986. //
  2987. // Less than a full page may be read from the file,
  2988. // only copy the amount that was read (the rest of
  2989. // the target page is zero already).
  2990. //
  2991. ASSERT (IoStatus.Information <= PAGE_4K);
  2992. RtlCopyMemory (StraddleVa,
  2993. HalfPage,
  2994. IoStatus.Information);
  2995. //
  2996. // The second 4K of this native page is already
  2997. // correctly zero as we allocated it that way.
  2998. //
  2999. MiUnmapSinglePage (StraddleVa);
  3000. ExFreePool (HalfPage);
  3001. }
  3002. #endif
  3003. }
  3004. }
  3005. else {
  3006. //
  3007. // No access pages.
  3008. // This can only occur for images with section alignment
  3009. // greater than the native PAGE_SIZE.
  3010. //
  3011. ASSERT (CheckSplitPages == FALSE);
  3012. MI_WRITE_INVALID_PTE (PointerPte, ZeroPte);
  3013. }
  3014. SectorOffset += PAGE_SIZE;
  3015. PointerPte += 1;
  3016. NextVa += PAGE_SIZE;
  3017. }
  3018. #if defined (_MIALT4K_)
  3019. //
  3020. // Ensure NextVa is bumped to the exact 4K (not native page size)
  3021. // boundary.
  3022. //
  3023. if (CheckSplitPages == TRUE) {
  3024. if (MI_ROUND_TO_SIZE (SectionTableEntry->VirtualAddress + SectionVirtualSize, PAGE_4K) != MI_ROUND_TO_SIZE (SectionTableEntry->VirtualAddress + SectionVirtualSize, PAGE_SIZE)) {
  3025. NextVa -= PAGE_4K;
  3026. }
  3027. }
  3028. #endif
  3029. }
  3030. SectionTableEntry += 1;
  3031. NumberOfSubsections -= 1;
  3032. }
  3033. ASSERT ((PointerPte > NewSegment->PrototypePte) &&
  3034. (PointerPte <= NewSegment->PrototypePte + NewSegment->TotalNumberOfPtes));
  3035. #if defined (_MIALT4K_)
  3036. if (CheckSplitPages == TRUE) {
  3037. if (BYTE_OFFSET (NextVa) == PAGE_4K) {
  3038. //
  3039. // The NextVa for the final subsection ended in the middle of a
  3040. // native page. Mark the prototype PTE as split and put the
  3041. // correct (no access) permission for the 2nd page into the
  3042. // subsection so it can be retrieved if/when the page is accessed.
  3043. //
  3044. //
  3045. // If the final subsection page was BSS, then create the page
  3046. // now and pagefile-back it so it can left as a prototype PTE.
  3047. // Otherwise on the first fault, we'll notice it is demand zero
  3048. // and allocate it that way with the writable bit clear in the
  3049. // native PTE (see the IA64 MI_MAKE_VALID_PTE macro for split
  3050. // pages). Unfortunately the alternate table PTE entry will
  3051. // already have been encoded copyonwrite, so the alternate fault
  3052. // handler will call the native fault code trying to write it,
  3053. // and get rejected with an AV since the native PTE will not be
  3054. // writable.
  3055. //
  3056. // By allocating a straddler page here, we'll keep the page as
  3057. // prototype-backed (instead of demand-zero), so the native fault
  3058. // code will handle it properly. Note this doesn't need to be done
  3059. // if the final subsection page was data (or text) instead of BSS,
  3060. // because then it is already going to remain prototype-backed
  3061. // during the fault processing.
  3062. //
  3063. ASSERT (PointerPte - 1 != NewSegment->PrototypePte);
  3064. ASSERT ((PointerPte - 1)->u.Hard.Valid == 0);
  3065. ASSERT ((PointerPte - 1)->u.Soft.SplitPermissions == 0);
  3066. if (FileHeader->NumberOfSections != 0) {
  3067. TempPteDemandZero.u.Long = 0;
  3068. TempPteDemandZero.u.Soft.Protection =
  3069. MiGetImageProtection ((SectionTableEntry - 1)->Characteristics);
  3070. if ((PointerPte - 1)->u.Long == TempPteDemandZero.u.Long) {
  3071. //
  3072. // Allocate a page of zeroes and place it into the
  3073. // last prototype PTE.
  3074. //
  3075. StraddleFrameNumber = MiGetPageForHeader (TRUE);
  3076. ASSERT (StraddleFrameNumber != 0);
  3077. Pfn1 = MI_PFN_ELEMENT (StraddleFrameNumber);
  3078. ASSERT (Pfn1->u1.Flink == 0);
  3079. Pfn1->u1.Flink = (PFN_NUMBER) ImagePages;
  3080. Pfn1->u2.Blink = (PFN_NUMBER) (PointerPte - 1);
  3081. ImagePages = Pfn1;
  3082. }
  3083. }
  3084. (PointerPte - 1)->u.Soft.SplitPermissions = 1;
  3085. Subsection->LastSplitPageProtection = MM_NOACCESS;
  3086. }
  3087. }
  3088. #endif
  3089. if (InvalidAlignmentAllowed == FALSE) {
  3090. //
  3091. // Account for the number of subsections that really are mapped.
  3092. //
  3093. ASSERT ((ImageAlignment >= PAGE_SIZE) || (CheckSplitPages == TRUE));
  3094. //
  3095. // If the file size is not as big as the image claimed to be,
  3096. // return an error.
  3097. //
  3098. if (ImageFileSize > EndOfFile.LowPart) {
  3099. //
  3100. // Invalid image size.
  3101. //
  3102. KdPrint(("MMCREASECT: invalid image size - file size %lx - image size %lx\n %Z\n",
  3103. EndOfFile.LowPart, ImageFileSize, &File->FileName));
  3104. Status = STATUS_INVALID_IMAGE_FORMAT;
  3105. MI_BAD_IMAGE (0x21);
  3106. goto BadPeImageSegment;
  3107. }
  3108. //
  3109. // The total number of PTEs was decremented as sections were built,
  3110. // make sure that there are less than 64k's worth at this point.
  3111. //
  3112. if (NumberOfPtes >= (ImageAlignment >> PAGE_SHIFT)) {
  3113. if ((CheckSplitPages == TRUE) && (NumberOfPtes == 0)) {
  3114. NOTHING;
  3115. }
  3116. else {
  3117. //
  3118. // Inconsistent image, size does not agree with object tables.
  3119. //
  3120. KdPrint(("MMCREASECT: invalid image - PTE left %lx, image name %Z\n",
  3121. NumberOfPtes, &File->FileName));
  3122. Status = STATUS_INVALID_IMAGE_FORMAT;
  3123. MI_BAD_IMAGE (0x22);
  3124. goto BadPeImageSegment;
  3125. }
  3126. }
  3127. //
  3128. // Set any remaining PTEs to no access.
  3129. //
  3130. if (NumberOfPtes != 0) {
  3131. MiZeroMemoryPte (PointerPte, NumberOfPtes);
  3132. }
  3133. if ((ExtendedHeader == NULL) &&
  3134. (SizeOfHeaders < PAGE_SIZE) &&
  3135. (CheckSplitPages == FALSE)) {
  3136. //
  3137. // Zero remaining portion of header.
  3138. //
  3139. RtlZeroMemory ((PVOID)((PCHAR)Base +
  3140. SizeOfHeaders),
  3141. PAGE_SIZE - SizeOfHeaders);
  3142. }
  3143. }
  3144. CommitCharged = NewSegment->NumberOfCommittedPages;
  3145. #if defined (_MIALT4K_)
  3146. //
  3147. // Charge commitment for all the straddler pages too. It is possible
  3148. // that some of them have been charged already depending on the types
  3149. // of each straddle, but there are so few of these it's not worth
  3150. // distinguishing this case.
  3151. //
  3152. Pfn1 = ImagePages;
  3153. do {
  3154. Pfn1 = (PMMPFN) Pfn1->u1.Flink;
  3155. CommitCharged += 1;
  3156. } while (Pfn1 != NULL);
  3157. NewSegment->NumberOfCommittedPages = CommitCharged;
  3158. #endif
  3159. if (CommitCharged != 0) {
  3160. //
  3161. // Commit the pages for the image section.
  3162. //
  3163. if (MiChargeCommitment (CommitCharged, NULL) == FALSE) {
  3164. MI_BAD_IMAGE (0x23);
  3165. Status = STATUS_COMMITMENT_LIMIT;
  3166. CommitCharged = 0;
  3167. goto BadPeImageSegment;
  3168. }
  3169. MM_TRACK_COMMIT (MM_DBG_COMMIT_IMAGE, CommitCharged);
  3170. Status = STATUS_SUCCESS;
  3171. InterlockedExchangeAddSizeT (&MmSharedCommit, CommitCharged);
  3172. }
  3173. PeReturnSuccess:
  3174. //
  3175. // Only images that are linked with subsections aligned to the native
  3176. // page size can be directly executed from ROM.
  3177. //
  3178. XipFile = FALSE;
  3179. XipFrameNumber = 0;
  3180. if ((FileAlignment == PAGE_SIZE - 1) && (XIPConfigured == TRUE)) {
  3181. Status = XIPLocatePages (File, &PhysicalAddress);
  3182. if (NT_SUCCESS(Status)) {
  3183. XipFrameNumber = (PFN_NUMBER) (PhysicalAddress.QuadPart >> PAGE_SHIFT);
  3184. //
  3185. // The small control area will need to be reallocated as a large
  3186. // one so the starting frame number can be inserted. Set XipFile
  3187. // to denote this.
  3188. //
  3189. XipFile = TRUE;
  3190. }
  3191. }
  3192. //
  3193. // If this image is global per session (or is going to be executed directly
  3194. // from ROM), then allocate a large control area. Note this doesn't need
  3195. // to be done for systemwide global control areas or non-global control
  3196. // areas.
  3197. //
  3198. GlobalPerSession = FALSE;
  3199. if ((ControlArea->u.Flags.GlobalMemory) &&
  3200. ((LoaderFlags & IMAGE_LOADER_FLAGS_SYSTEM_GLOBAL) == 0)) {
  3201. GlobalPerSession = TRUE;
  3202. }
  3203. if ((XipFile == TRUE) || (GlobalPerSession == TRUE)) {
  3204. LargeControlArea = ExAllocatePoolWithTag (NonPagedPool,
  3205. (ULONG)(sizeof(LARGE_CONTROL_AREA) +
  3206. (sizeof(SUBSECTION) *
  3207. SubsectionsAllocated)),
  3208. 'iCmM');
  3209. if (LargeControlArea == NULL) {
  3210. //
  3211. // The requested pool could not be allocated. If the image is
  3212. // execute-in-place only (ie: not global per session), then just
  3213. // execute it normally instead of inplace (to avoid not executing
  3214. // it at all).
  3215. //
  3216. if ((XipFile == TRUE) && (GlobalPerSession == FALSE)) {
  3217. goto SkipLargeControlArea;
  3218. }
  3219. MI_BAD_IMAGE (0x24);
  3220. Status = STATUS_INSUFFICIENT_RESOURCES;
  3221. goto BadPeImageSegment;
  3222. }
  3223. //
  3224. // Copy the normal control area into our larger one, fix the linkages,
  3225. // Fill in the additional fields in the new one and free the old one.
  3226. //
  3227. RtlCopyMemory (LargeControlArea, ControlArea, sizeof(CONTROL_AREA));
  3228. ASSERT (ControlArea->u.Flags.GlobalOnlyPerSession == 0);
  3229. if (XipFile == TRUE) {
  3230. //
  3231. // Mark the large control area accordingly. If we can't, then
  3232. // throw it away and use the small control area and execute from
  3233. // RAM instead.
  3234. //
  3235. if (MiMakeControlAreaRom (File, LargeControlArea, XipFrameNumber) == FALSE) {
  3236. if (GlobalPerSession == FALSE) {
  3237. ExFreePool (LargeControlArea);
  3238. goto SkipLargeControlArea;
  3239. }
  3240. }
  3241. }
  3242. Subsection = (PSUBSECTION)(ControlArea + 1);
  3243. NewSubsection = (PSUBSECTION)(LargeControlArea + 1);
  3244. for (i = 0; i < SubsectionsAllocated; i += 1) {
  3245. RtlCopyMemory (NewSubsection, Subsection, sizeof(SUBSECTION));
  3246. NewSubsection->ControlArea = (PCONTROL_AREA) LargeControlArea;
  3247. NewSubsection->NextSubsection = (NewSubsection + 1);
  3248. PointerPte = NewSegment->PrototypePte;
  3249. TempPte.u.Long = MiGetSubsectionAddressForPte (NewSubsection);
  3250. TempPte.u.Soft.Prototype = 1;
  3251. for (j = 0; j < NewSegment->TotalNumberOfPtes; j += 1) {
  3252. ASSERT (PointerPte->u.Hard.Valid == 0);
  3253. if ((PointerPte->u.Soft.Prototype == 1) &&
  3254. (MiGetSubsectionAddress (PointerPte) == Subsection)) {
  3255. OriginalProtection = MI_GET_PROTECTION_FROM_SOFT_PTE (PointerPte);
  3256. #if defined (_MIALT4K_)
  3257. TempPte.u.Soft.SplitPermissions =
  3258. PointerPte->u.Soft.SplitPermissions;
  3259. #endif
  3260. TempPte.u.Soft.Protection = OriginalProtection;
  3261. MI_WRITE_INVALID_PTE (PointerPte, TempPte);
  3262. }
  3263. PointerPte += 1;
  3264. }
  3265. Subsection += 1;
  3266. NewSubsection += 1;
  3267. }
  3268. (NewSubsection - 1)->NextSubsection = NULL;
  3269. NewSegment->ControlArea = (PCONTROL_AREA) LargeControlArea;
  3270. if (GlobalPerSession == TRUE) {
  3271. LargeControlArea->u.Flags.GlobalOnlyPerSession = 1;
  3272. LargeControlArea->SessionId = 0;
  3273. InitializeListHead (&LargeControlArea->UserGlobalList);
  3274. }
  3275. ExFreePool (ControlArea);
  3276. ControlArea = (PCONTROL_AREA) LargeControlArea;
  3277. }
  3278. SkipLargeControlArea:
  3279. MiUnmapImageHeaderInHyperSpace ();
  3280. //
  3281. // Turn the image header and any straddler pages into transition pages
  3282. // within the prototype PTEs.
  3283. //
  3284. // Note this could not be done earlier because otherwise these pages
  3285. // could have been trimmed (and reused) before we even finished
  3286. // initializing the segment. This would be especially bad for the page
  3287. // that contains the PE header because we'd still have it mapped and
  3288. // be looking at its section header entries, etc while the page
  3289. // was being reused !
  3290. //
  3291. PointerPte = NewSegment->PrototypePte;
  3292. Pfn1 = ImagePages;
  3293. do {
  3294. Pfn2 = (PMMPFN) Pfn1->u1.Flink;
  3295. PointerPte = (PMMPTE) Pfn1->u2.Blink;
  3296. //
  3297. // Note straddler pages must always get marked modified. The PE
  3298. // header only gets marked modified if it was shared with a straddler.
  3299. //
  3300. MarkModified = TRUE;
  3301. //
  3302. // The list is reversed, so the last entry (Pfn2 == NULL) is
  3303. // the PE header.
  3304. //
  3305. if (Pfn2 == NULL) {
  3306. ASSERT (PointerPte == NewSegment->PrototypePte);
  3307. if (PageFrameNumber != 0) {
  3308. //
  3309. // The PE header was not shared with a straddler.
  3310. //
  3311. MarkModified = FALSE;
  3312. }
  3313. }
  3314. ASSERT ((PointerPte >= NewSegment->PrototypePte) &&
  3315. (PointerPte < NewSegment->PrototypePte + NewSegment->TotalNumberOfPtes));
  3316. //
  3317. // Mark straddler pages as modified so they will be written
  3318. // and retrieved from the pagefile (and never from the
  3319. // filesystem again). Put the straddler page on the modified
  3320. // list and set the transition bit in the prototype PTE.
  3321. //
  3322. MiUpdateImageHeaderPage (PointerPte,
  3323. Pfn1 - MmPfnDatabase,
  3324. ControlArea,
  3325. MarkModified);
  3326. MarkModified = TRUE;
  3327. Pfn1 = Pfn2;
  3328. } while (Pfn1 != NULL);
  3329. if (ExtendedHeader != NULL) {
  3330. ExFreePool (ExtendedHeader);
  3331. }
  3332. return STATUS_SUCCESS;
  3333. //
  3334. // Error returns from image verification.
  3335. //
  3336. BadPeImageSegment:
  3337. ASSERT (!NT_SUCCESS (Status));
  3338. ASSERT ((ControlArea == NULL) || (ControlArea->NumberOfPfnReferences == 0));
  3339. if (NewSegment != NULL) {
  3340. if (CommitCharged != 0) {
  3341. ASSERT (CommitCharged == NewSegment->NumberOfCommittedPages);
  3342. MiReturnCommitment (CommitCharged);
  3343. MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_IMAGE_NO_LARGE_CA, CommitCharged);
  3344. InterlockedExchangeAddSizeT (&MmSharedCommit, 0 - CommitCharged);
  3345. }
  3346. #if DBG
  3347. PointerPte = NewSegment->PrototypePte;
  3348. for (i = 0; i < NewSegment->TotalNumberOfPtes; i += 1) {
  3349. TempPte.u.Long = PointerPte->u.Long;
  3350. ASSERT ((TempPte.u.Hard.Valid == 0) &&
  3351. ((TempPte.u.Soft.Prototype == 1) ||
  3352. (TempPte.u.Soft.Transition == 0)));
  3353. PointerPte += 1;
  3354. }
  3355. #endif
  3356. }
  3357. ASSERT ((ControlArea == NULL) || (ControlArea->NumberOfPfnReferences == 0));
  3358. if (Base != NULL) {
  3359. MiUnmapImageHeaderInHyperSpace ();
  3360. }
  3361. Pfn1 = ImagePages;
  3362. do {
  3363. Pfn2 = (PMMPFN) Pfn1->u1.Flink;
  3364. Pfn1->u2.Blink = 0; // Clear prototype PTE pointer before freeing
  3365. MiRemoveImageHeaderPage (Pfn1 - MmPfnDatabase);
  3366. Pfn1 = Pfn2;
  3367. } while (Pfn1 != NULL);
  3368. ASSERT ((ControlArea == NULL) || (ControlArea->NumberOfPfnReferences == 0));
  3369. if (NewSegment != NULL) {
  3370. ExFreePool (NewSegment);
  3371. }
  3372. if (ControlArea != NULL) {
  3373. ExFreePool (ControlArea);
  3374. }
  3375. if (ExtendedHeader != NULL) {
  3376. ExFreePool (ExtendedHeader);
  3377. }
  3378. return Status;
  3379. }
  3380. LOGICAL
  3381. MiCheckDosCalls (
  3382. IN PIMAGE_OS2_HEADER Os2Header,
  3383. IN ULONG HeaderSize
  3384. )
  3385. /*++
  3386. Routine Description:
  3387. This routine checks for DOS calls.
  3388. Arguments:
  3389. Os2Header - Supplies a kernelmode pointer to the OS2 header.
  3390. HeaderSize - Supplies the length in bytes of the mapped header.
  3391. Return Value:
  3392. Returns TRUE if this is a Win-16 image, FALSE if not.
  3393. --*/
  3394. {
  3395. PUCHAR ImportTable;
  3396. UCHAR EntrySize;
  3397. USHORT ModuleCount;
  3398. USHORT ModuleSize;
  3399. USHORT i;
  3400. PUSHORT ModuleTable;
  3401. PAGED_CODE();
  3402. //
  3403. // If there are no modules to check return immediately.
  3404. //
  3405. ModuleCount = Os2Header->ne_cmod;
  3406. if (ModuleCount == 0) {
  3407. return FALSE;
  3408. }
  3409. //
  3410. // exe headers are notorious for having junk values for offsets
  3411. // in the import table and module table so the header must be very
  3412. // carefully validated.
  3413. //
  3414. //
  3415. // Find out where the Module ref table is. Mod table has two byte
  3416. // for each entry in import table. These two bytes tell the offset
  3417. // in the import table for that entry.
  3418. //
  3419. ModuleTable = (PUSHORT)((PCHAR)Os2Header + (ULONG)Os2Header->ne_modtab);
  3420. //
  3421. // Make sure that the module table fits within the passed-in header.
  3422. // Note that each module table entry is 2 bytes long.
  3423. //
  3424. if (((ULONG)Os2Header->ne_modtab + (ModuleCount * 2)) > HeaderSize) {
  3425. return FALSE;
  3426. }
  3427. //
  3428. // Now search individual entries for DOSCALLs.
  3429. //
  3430. for (i = 0; i < ModuleCount; i += 1) {
  3431. ModuleSize = *((UNALIGNED USHORT *)ModuleTable);
  3432. //
  3433. // Import table has count byte followed by the string where count
  3434. // is the string length.
  3435. //
  3436. ImportTable = (PUCHAR)((PCHAR)Os2Header +
  3437. (ULONG)Os2Header->ne_imptab + (ULONG)ModuleSize);
  3438. //
  3439. // Make sure the offset is within the valid range.
  3440. //
  3441. if (((ULONG)Os2Header->ne_imptab + (ULONG)ModuleSize) >= HeaderSize) {
  3442. return FALSE;
  3443. }
  3444. EntrySize = *ImportTable++;
  3445. //
  3446. // 0 is a bad size, bail out.
  3447. //
  3448. if (EntrySize == 0) {
  3449. return FALSE;
  3450. }
  3451. //
  3452. // Make sure the offset is within the valid range.
  3453. // The sizeof(UCHAR) is included in the check because ImportTable
  3454. // was incremented above and is used in the RtlEqualMemory
  3455. // comparison below.
  3456. //
  3457. if (((ULONG)Os2Header->ne_imptab + (ULONG)ModuleSize +
  3458. (ULONG)EntrySize + sizeof(UCHAR)) > HeaderSize) {
  3459. return FALSE;
  3460. }
  3461. //
  3462. // If size matches compare DOSCALLS.
  3463. //
  3464. if (EntrySize == 8) {
  3465. if (RtlEqualMemory (ImportTable, "DOSCALLS", 8)) {
  3466. return TRUE;
  3467. }
  3468. }
  3469. //
  3470. // Move on to the next module table entry. Each entry is 2 bytes.
  3471. //
  3472. ModuleTable = (PUSHORT)((PCHAR)ModuleTable + 2);
  3473. }
  3474. return FALSE;
  3475. }
  3476. NTSTATUS
  3477. MiVerifyImageHeader (
  3478. IN PIMAGE_NT_HEADERS NtHeader,
  3479. IN PIMAGE_DOS_HEADER DosHeader,
  3480. IN ULONG NtHeaderSize
  3481. )
  3482. /*++
  3483. Routine Description:
  3484. This function checks for various inconsistencies in the image header.
  3485. Arguments:
  3486. NtHeader - Supplies a pointer to the NT header of the image.
  3487. DosHeader - Supplies a pointer to the DOS header of the image.
  3488. NtHeaderSize - Supplies the size in bytes of the NT header.
  3489. Return Value:
  3490. NTSTATUS.
  3491. --*/
  3492. {
  3493. PCONFIGPHARLAP PharLapConfigured;
  3494. PUCHAR pb;
  3495. LONG pResTableAddress;
  3496. PAGED_CODE();
  3497. if (NtHeader->Signature != IMAGE_NT_SIGNATURE) {
  3498. if ((USHORT)NtHeader->Signature == (USHORT)IMAGE_OS2_SIGNATURE) {
  3499. //
  3500. // Check to see if this is a win-16 image.
  3501. //
  3502. if ((!MiCheckDosCalls ((PIMAGE_OS2_HEADER)NtHeader, NtHeaderSize)) &&
  3503. ((((PIMAGE_OS2_HEADER)NtHeader)->ne_exetyp == 2)
  3504. ||
  3505. ((((PIMAGE_OS2_HEADER)NtHeader)->ne_exetyp == 0) &&
  3506. (((((PIMAGE_OS2_HEADER)NtHeader)->ne_expver & 0xff00) ==
  3507. 0x200) ||
  3508. ((((PIMAGE_OS2_HEADER)NtHeader)->ne_expver & 0xff00) ==
  3509. 0x300))))) {
  3510. //
  3511. // This is a win-16 image.
  3512. //
  3513. return STATUS_INVALID_IMAGE_WIN_16;
  3514. }
  3515. // The following OS/2 headers types go to NTDOS
  3516. //
  3517. // - exetype == 5 means binary is for Dos 4.0.
  3518. // e.g Borland Dos extender type
  3519. //
  3520. // - OS/2 apps which have no import table entries
  3521. // cannot be meant for the OS/2 ss.
  3522. // e.g. QuickC for dos binaries
  3523. //
  3524. // - "old" Borland Dosx BC++ 3.x, Paradox 4.x
  3525. // exe type == 1
  3526. // DosHeader->e_cs * 16 + DosHeader->e_ip + 0x200 - 10
  3527. // contains the string " mode EXE$"
  3528. // but import table is empty, so we don't make special check
  3529. //
  3530. if (((PIMAGE_OS2_HEADER)NtHeader)->ne_exetyp == 5 ||
  3531. ((PIMAGE_OS2_HEADER)NtHeader)->ne_enttab ==
  3532. ((PIMAGE_OS2_HEADER)NtHeader)->ne_imptab) {
  3533. return STATUS_INVALID_IMAGE_PROTECT;
  3534. }
  3535. //
  3536. // Borland Dosx types: exe type 1
  3537. //
  3538. // - "new" Borland Dosx BP7.0
  3539. // exe type == 1
  3540. // DosHeader + 0x200 contains the string "16STUB"
  3541. // 0x200 happens to be e_parhdr*16
  3542. //
  3543. if (((PIMAGE_OS2_HEADER)NtHeader)->ne_exetyp == 1 &&
  3544. RtlEqualMemory((PUCHAR)DosHeader + 0x200, "16STUB", 6)) {
  3545. return STATUS_INVALID_IMAGE_PROTECT;
  3546. }
  3547. //
  3548. // Check for PharLap extended header which we run as a dos app.
  3549. // The PharLap config block is pointed to by the SizeofHeader
  3550. // field in the DosHdr.
  3551. // The following algorithm for detecting a pharlap exe
  3552. // was recommended by PharLap Software Inc.
  3553. //
  3554. PharLapConfigured =(PCONFIGPHARLAP) ((PCHAR)DosHeader +
  3555. ((ULONG)DosHeader->e_cparhdr << 4));
  3556. if ((PCHAR)PharLapConfigured <
  3557. (PCHAR)DosHeader + PAGE_SIZE - sizeof(CONFIGPHARLAP)) {
  3558. if (RtlEqualMemory(&PharLapConfigured->uchCopyRight[0x18],
  3559. "Phar Lap Software, Inc.", 24) &&
  3560. (PharLapConfigured->usSign == 0x4b50 || // stub loader type 2
  3561. PharLapConfigured->usSign == 0x4f50 || // bindable 286|DosExtender
  3562. PharLapConfigured->usSign == 0x5650 )) // bindable 286|DosExtender (Adv)
  3563. {
  3564. return STATUS_INVALID_IMAGE_PROTECT;
  3565. }
  3566. }
  3567. //
  3568. // Check for Rational extended header which we run as a dos app.
  3569. // We look for the rational copyright at:
  3570. // wCopyRight = *(DosHeader->e_cparhdr*16 + 30h)
  3571. // pCopyRight = wCopyRight + DosHeader->e_cparhdr*16
  3572. // "Copyright (C) Rational Systems, Inc."
  3573. //
  3574. pb = ((PUCHAR)DosHeader + ((ULONG)DosHeader->e_cparhdr << 4));
  3575. if ((ULONG_PTR)pb < (ULONG_PTR)DosHeader + PAGE_SIZE - 0x30 - sizeof(USHORT)) {
  3576. pb += *(PUSHORT)(pb + 0x30);
  3577. if ((ULONG_PTR)pb < (ULONG_PTR)DosHeader + PAGE_SIZE - 36 &&
  3578. RtlEqualMemory(pb,
  3579. "Copyright (C) Rational Systems, Inc.",
  3580. 36))
  3581. {
  3582. return STATUS_INVALID_IMAGE_PROTECT;
  3583. }
  3584. }
  3585. //
  3586. // Check for lotus 123 family of applications. Starting
  3587. // with 123 3.0 (till recently shipped 123 3.4), every
  3588. // exe header is bound but is meant for DOS. This can
  3589. // be checked via, a string signature in the extended
  3590. // header. <len byte>"1-2-3 Preloader" is the string
  3591. // at ne_nrestab offset.
  3592. //
  3593. pResTableAddress = ((PIMAGE_OS2_HEADER)NtHeader)->ne_nrestab;
  3594. if (pResTableAddress > DosHeader->e_lfanew &&
  3595. ((ULONG)((pResTableAddress+16) - DosHeader->e_lfanew) <
  3596. NtHeaderSize) &&
  3597. RtlEqualMemory(
  3598. ((PUCHAR)NtHeader + 1 +
  3599. (ULONG)(pResTableAddress - DosHeader->e_lfanew)),
  3600. "1-2-3 Preloader",
  3601. 15)) {
  3602. return STATUS_INVALID_IMAGE_PROTECT;
  3603. }
  3604. return STATUS_INVALID_IMAGE_NE_FORMAT;
  3605. }
  3606. if ((USHORT)NtHeader->Signature == (USHORT)IMAGE_OS2_SIGNATURE_LE) {
  3607. //
  3608. // This is a LE (OS/2) image. We don't support it, so give it to
  3609. // DOS subsystem. There are cases (Rbase.exe) which have a LE
  3610. // header but actually it is suppose to run under DOS. When we
  3611. // do support LE format, some work needs to be done here to
  3612. // decide whether to give it to VDM or OS/2.
  3613. //
  3614. return STATUS_INVALID_IMAGE_PROTECT;
  3615. }
  3616. return STATUS_INVALID_IMAGE_PROTECT;
  3617. }
  3618. if ((NtHeader->FileHeader.Machine == 0) &&
  3619. (NtHeader->FileHeader.SizeOfOptionalHeader == 0)) {
  3620. //
  3621. // This is a bogus DOS app which has a 32-bit portion
  3622. // masquerading as a PE image.
  3623. //
  3624. return STATUS_INVALID_IMAGE_PROTECT;
  3625. }
  3626. if (!(NtHeader->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE)) {
  3627. return STATUS_INVALID_IMAGE_FORMAT;
  3628. }
  3629. #ifdef i386
  3630. //
  3631. // Make sure the image header is aligned on a Long word boundary.
  3632. //
  3633. if (((ULONG_PTR)NtHeader & 3) != 0) {
  3634. return STATUS_INVALID_IMAGE_FORMAT;
  3635. }
  3636. #endif
  3637. #define VALIDATE_NTHEADER(Hdr) { \
  3638. /* File alignment must be multiple of 512 and power of 2. */ \
  3639. if (((((Hdr)->OptionalHeader).FileAlignment & 511) != 0) && \
  3640. (((Hdr)->OptionalHeader).FileAlignment != \
  3641. ((Hdr)->OptionalHeader).SectionAlignment)) { \
  3642. return STATUS_INVALID_IMAGE_FORMAT; \
  3643. } \
  3644. \
  3645. if (((Hdr)->OptionalHeader).FileAlignment == 0) { \
  3646. return STATUS_INVALID_IMAGE_FORMAT; \
  3647. } \
  3648. \
  3649. if (((((Hdr)->OptionalHeader).FileAlignment - 1) & \
  3650. ((Hdr)->OptionalHeader).FileAlignment) != 0) { \
  3651. return STATUS_INVALID_IMAGE_FORMAT; \
  3652. } \
  3653. \
  3654. if (((Hdr)->OptionalHeader).SectionAlignment < ((Hdr)->OptionalHeader).FileAlignment) { \
  3655. return STATUS_INVALID_IMAGE_FORMAT; \
  3656. } \
  3657. \
  3658. if (((Hdr)->OptionalHeader).SizeOfImage > MM_SIZE_OF_LARGEST_IMAGE) { \
  3659. return STATUS_INVALID_IMAGE_FORMAT; \
  3660. } \
  3661. \
  3662. if ((Hdr)->FileHeader.NumberOfSections > MM_MAXIMUM_IMAGE_SECTIONS) { \
  3663. return STATUS_INVALID_IMAGE_FORMAT; \
  3664. } \
  3665. \
  3666. if (((Hdr)->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) && \
  3667. !((Hdr)->FileHeader.Machine == IMAGE_FILE_MACHINE_I386)) { \
  3668. return STATUS_INVALID_IMAGE_FORMAT; \
  3669. } \
  3670. \
  3671. if (((Hdr)->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) && \
  3672. !(((Hdr)->FileHeader.Machine == IMAGE_FILE_MACHINE_IA64) || \
  3673. ((Hdr)->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64))) { \
  3674. return STATUS_INVALID_IMAGE_FORMAT; \
  3675. } \
  3676. }
  3677. if (NtHeader->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) {
  3678. //
  3679. // Image doesn't have the right magic value in its optional header.
  3680. //
  3681. #if defined (_WIN64)
  3682. if (NtHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
  3683. //
  3684. // PE32 image. Validate it as such.
  3685. //
  3686. PIMAGE_NT_HEADERS32 NtHeader32 = (PIMAGE_NT_HEADERS32)NtHeader;
  3687. VALIDATE_NTHEADER(NtHeader32);
  3688. return STATUS_SUCCESS;
  3689. }
  3690. #else /* !defined(_WIN64) */
  3691. if (NtHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
  3692. //
  3693. // 64bit image on a 32bit machine.
  3694. //
  3695. return STATUS_INVALID_IMAGE_WIN_64;
  3696. }
  3697. #endif
  3698. return STATUS_INVALID_IMAGE_FORMAT;
  3699. }
  3700. VALIDATE_NTHEADER(NtHeader);
  3701. #undef VALIDATE_NTHEADER
  3702. return STATUS_SUCCESS;
  3703. }
  3704. NTSTATUS
  3705. MiCreateDataFileMap (
  3706. IN PFILE_OBJECT File,
  3707. OUT PSEGMENT *Segment,
  3708. IN PUINT64 MaximumSize,
  3709. IN ULONG SectionPageProtection,
  3710. IN ULONG AllocationAttributes,
  3711. IN ULONG IgnoreFileSizing
  3712. )
  3713. /*++
  3714. Routine Description:
  3715. This function creates the necessary structures to allow the mapping
  3716. of a data file.
  3717. The data file is accessed to verify desired access, a segment
  3718. object is created and initialized.
  3719. Arguments:
  3720. File - Supplies the file object for the image file.
  3721. Segment - Returns the segment object.
  3722. MaximumSize - Supplies the maximum size for the mapping.
  3723. SectionPageProtection - Supplies the initial page protection.
  3724. AllocationAttributes - Supplies the allocation attributes for the mapping.
  3725. IgnoreFileSizing - Supplies TRUE if the cache manager is specifying the
  3726. file size and so it does not need to be validated.
  3727. Return Value:
  3728. NTSTATUS.
  3729. --*/
  3730. {
  3731. NTSTATUS Status;
  3732. ULONG j;
  3733. ULONG Size;
  3734. UINT64 PartialSize;
  3735. PCONTROL_AREA ControlArea;
  3736. PLARGE_CONTROL_AREA LargeControlArea;
  3737. PMAPPED_FILE_SEGMENT NewSegment;
  3738. PMSUBSECTION Subsection;
  3739. PMSUBSECTION ExtendedSubsection;
  3740. PMSUBSECTION LargeExtendedSubsection;
  3741. MMPTE TempPte;
  3742. UINT64 EndOfFile;
  3743. UINT64 LastFileChunk;
  3744. UINT64 FileOffset;
  3745. UINT64 NumberOfPtesForEntireFile;
  3746. ULONG ExtendedSubsections;
  3747. PMSUBSECTION Last;
  3748. ULONG NumberOfNewSubsections;
  3749. SIZE_T AllocationFragment;
  3750. PHYSICAL_ADDRESS PhysicalAddress;
  3751. PFN_NUMBER PageFrameNumber;
  3752. PAGED_CODE();
  3753. ExtendedSubsections = 0;
  3754. // *************************************************************
  3755. // Create mapped file section.
  3756. // *************************************************************
  3757. if (!IgnoreFileSizing) {
  3758. Status = FsRtlGetFileSize (File, (PLARGE_INTEGER)&EndOfFile);
  3759. if (Status == STATUS_FILE_IS_A_DIRECTORY) {
  3760. //
  3761. // Can't map a directory as a section. Return error.
  3762. //
  3763. return STATUS_INVALID_FILE_FOR_SECTION;
  3764. }
  3765. if (!NT_SUCCESS (Status)) {
  3766. return Status;
  3767. }
  3768. if (EndOfFile == 0 && *MaximumSize == 0) {
  3769. //
  3770. // Can't map a zero length without specifying the maximum
  3771. // size as non-zero.
  3772. //
  3773. return STATUS_MAPPED_FILE_SIZE_ZERO;
  3774. }
  3775. //
  3776. // Make sure this file is big enough for the section.
  3777. //
  3778. if (*MaximumSize > EndOfFile) {
  3779. //
  3780. // If the maximum size is greater than the end-of-file,
  3781. // and the user did not request page_write or page_execute_readwrite
  3782. // to the section, reject the request.
  3783. //
  3784. if (((SectionPageProtection & PAGE_READWRITE) |
  3785. (SectionPageProtection & PAGE_EXECUTE_READWRITE)) == 0) {
  3786. return STATUS_SECTION_TOO_BIG;
  3787. }
  3788. //
  3789. // Check to make sure that the allocation size large enough
  3790. // to contain all the data, if not set a new allocation size.
  3791. //
  3792. EndOfFile = *MaximumSize;
  3793. Status = FsRtlSetFileSize (File, (PLARGE_INTEGER)&EndOfFile);
  3794. if (!NT_SUCCESS (Status)) {
  3795. return Status;
  3796. }
  3797. }
  3798. }
  3799. else {
  3800. //
  3801. // Ignore the file size, this call is from the cache manager.
  3802. //
  3803. EndOfFile = *MaximumSize;
  3804. }
  3805. //
  3806. // Calculate the number of prototype PTE chunks to build for this section.
  3807. // A subsection is also allocated for each chunk as all the prototype PTEs
  3808. // in any given chunk are initially encoded to point at the same subsection.
  3809. //
  3810. // The maximum total section size is 16PB (2^54). This is because the
  3811. // StartingSector4132 field in each subsection, ie: 2^42-1 bits of file
  3812. // offset where the offset is in 4K (not pagesize) units. Thus, a
  3813. // subsection may describe a *BYTE* file start offset of maximum
  3814. // 2^54 - 4K.
  3815. //
  3816. // Each subsection can span at most 16TB - 64K. This is because the
  3817. // NumberOfFullSectors and various other fields in the subsection are
  3818. // ULONGs. In reality, this is a nonissue as far as maximum section size
  3819. // is concerned because any number of subsections can be chained together
  3820. // and in fact, subsections are allocated to span less to facilitate
  3821. // efficient dynamic prototype PTE trimming and reconstruction.
  3822. //
  3823. if (EndOfFile > MI_MAXIMUM_SECTION_SIZE) {
  3824. return STATUS_SECTION_TOO_BIG;
  3825. }
  3826. NumberOfPtesForEntireFile = (EndOfFile + PAGE_SIZE - 1) >> PAGE_SHIFT;
  3827. NewSegment = ExAllocatePoolWithTag (PagedPool,
  3828. sizeof(MAPPED_FILE_SEGMENT),
  3829. 'mSmM');
  3830. if (NewSegment == NULL) {
  3831. return STATUS_INSUFFICIENT_RESOURCES;
  3832. }
  3833. //
  3834. // Allocate the subsection memory in smaller sizes so the corresponding
  3835. // prototype PTEs can be trimmed later if paged pool virtual address
  3836. // space becomes scarce. Note the size is snapped locally so it can
  3837. // be changed dynamically without locking.
  3838. //
  3839. AllocationFragment = MmAllocationFragment;
  3840. ASSERT (MiGetByteOffset (AllocationFragment) == 0);
  3841. ASSERT (AllocationFragment >= PAGE_SIZE);
  3842. ASSERT64 (AllocationFragment < _4gb);
  3843. Size = (ULONG) AllocationFragment;
  3844. PartialSize = NumberOfPtesForEntireFile * sizeof(MMPTE);
  3845. NumberOfNewSubsections = 0;
  3846. ExtendedSubsection = NULL;
  3847. //
  3848. // Initializing Last is not needed for correctness, but without it the
  3849. // compiler cannot compile this code W4 to check for use of uninitialized
  3850. // variables.
  3851. //
  3852. Last = NULL;
  3853. ControlArea = (PCONTROL_AREA)File->SectionObjectPointer->DataSectionObject;
  3854. do {
  3855. if (PartialSize < (UINT64) AllocationFragment) {
  3856. PartialSize = (UINT64) ROUND_TO_PAGES (PartialSize);
  3857. Size = (ULONG) PartialSize;
  3858. }
  3859. if (ExtendedSubsection == NULL) {
  3860. ExtendedSubsection = (PMSUBSECTION)(ControlArea + 1);
  3861. //
  3862. // Control area and first subsection were zeroed at allocation time.
  3863. //
  3864. }
  3865. else {
  3866. ExtendedSubsection = ExAllocatePoolWithTag (NonPagedPool,
  3867. sizeof(MSUBSECTION),
  3868. 'cSmM');
  3869. if (ExtendedSubsection == NULL) {
  3870. ExFreePool (NewSegment);
  3871. //
  3872. // Free all the previous allocations and return an error.
  3873. //
  3874. ExtendedSubsection = (PMSUBSECTION)(ControlArea + 1);
  3875. ExtendedSubsection = (PMSUBSECTION) ExtendedSubsection->NextSubsection;
  3876. while (ExtendedSubsection != NULL) {
  3877. Last = (PMSUBSECTION) ExtendedSubsection->NextSubsection;
  3878. ExFreePool (ExtendedSubsection);
  3879. ExtendedSubsection = Last;
  3880. }
  3881. return STATUS_INSUFFICIENT_RESOURCES;
  3882. }
  3883. RtlZeroMemory (ExtendedSubsection, sizeof(MSUBSECTION));
  3884. Last->NextSubsection = (PSUBSECTION) ExtendedSubsection;
  3885. }
  3886. NumberOfNewSubsections += 1;
  3887. ExtendedSubsection->PtesInSubsection = Size / sizeof(MMPTE);
  3888. Last = ExtendedSubsection;
  3889. PartialSize -= Size;
  3890. } while (PartialSize != 0);
  3891. *Segment = (PSEGMENT) NewSegment;
  3892. RtlZeroMemory (NewSegment, sizeof(MAPPED_FILE_SEGMENT));
  3893. NewSegment->LastSubsectionHint = ExtendedSubsection;
  3894. //
  3895. // Control area and first subsection were zeroed at allocation time.
  3896. //
  3897. ControlArea->Segment = (PSEGMENT) NewSegment;
  3898. ControlArea->NumberOfSectionReferences = 1;
  3899. if (IgnoreFileSizing == FALSE) {
  3900. //
  3901. // This reference is not from the cache manager.
  3902. //
  3903. ControlArea->NumberOfUserReferences = 1;
  3904. }
  3905. else {
  3906. //
  3907. // Set the was purged flag to indicate that the
  3908. // file size was not explicitly set.
  3909. //
  3910. ControlArea->u.Flags.WasPurged = 1;
  3911. }
  3912. ControlArea->u.Flags.BeingCreated = 1;
  3913. ControlArea->u.Flags.File = 1;
  3914. if (FILE_REMOTE_DEVICE & File->DeviceObject->Characteristics) {
  3915. //
  3916. // This file resides on a redirected drive.
  3917. //
  3918. ControlArea->u.Flags.Networked = 1;
  3919. }
  3920. if (AllocationAttributes & SEC_NOCACHE) {
  3921. ControlArea->u.Flags.NoCache = 1;
  3922. }
  3923. ControlArea->FilePointer = File;
  3924. ASSERT (ControlArea->u.Flags.GlobalOnlyPerSession == 0);
  3925. Subsection = (PMSUBSECTION)(ControlArea + 1);
  3926. //
  3927. // Loop through all the subsections and fill in the PTEs.
  3928. //
  3929. TempPte.u.Long = MiGetSubsectionAddressForPte (Subsection);
  3930. TempPte.u.Soft.Prototype = 1;
  3931. //
  3932. // Set all the PTEs to the execute-read-write protection.
  3933. // The section will control access to these and the segment
  3934. // must provide a method to allow other users to map the file
  3935. // for various protections.
  3936. //
  3937. TempPte.u.Soft.Protection = MM_EXECUTE_READWRITE;
  3938. NewSegment->ControlArea = ControlArea;
  3939. NewSegment->SizeOfSegment = EndOfFile;
  3940. NewSegment->TotalNumberOfPtes = (ULONG) NumberOfPtesForEntireFile;
  3941. if (NumberOfPtesForEntireFile >= 0x100000000) {
  3942. NewSegment->SegmentFlags.TotalNumberOfPtes4132 = (ULONG_PTR)(NumberOfPtesForEntireFile >> 32);
  3943. }
  3944. NewSegment->SegmentPteTemplate = TempPte;
  3945. if (Subsection->NextSubsection != NULL) {
  3946. //
  3947. // Multiple segments and subsections.
  3948. // Align first so it is a multiple of the allocation size.
  3949. //
  3950. NewSegment->NonExtendedPtes =
  3951. (Subsection->PtesInSubsection & ~(((ULONG)AllocationFragment >> PAGE_SHIFT) - 1));
  3952. }
  3953. else {
  3954. NewSegment->NonExtendedPtes = NewSegment->TotalNumberOfPtes;
  3955. }
  3956. Subsection->PtesInSubsection = NewSegment->NonExtendedPtes;
  3957. FileOffset = 0;
  3958. do {
  3959. //
  3960. // Loop through all the subsections to initialize them.
  3961. //
  3962. Subsection->ControlArea = ControlArea;
  3963. Mi4KStartForSubsection(&FileOffset, Subsection);
  3964. Subsection->u.SubsectionFlags.Protection = MM_EXECUTE_READWRITE;
  3965. if (Subsection->NextSubsection == NULL) {
  3966. LastFileChunk = (EndOfFile >> MM4K_SHIFT) - FileOffset;
  3967. //
  3968. // Note this next line restricts the number of bytes mapped by
  3969. // a single subsection to 16TB-4K. Multiple subsections can always
  3970. // be chained together to support an overall file of size 16K TB.
  3971. //
  3972. Subsection->NumberOfFullSectors = (ULONG)LastFileChunk;
  3973. Subsection->u.SubsectionFlags.SectorEndOffset =
  3974. (ULONG) EndOfFile & MM4K_MASK;
  3975. j = Subsection->PtesInSubsection;
  3976. Subsection->PtesInSubsection = (ULONG)(
  3977. NumberOfPtesForEntireFile -
  3978. (FileOffset >> (PAGE_SHIFT - MM4K_SHIFT)));
  3979. MI_CHECK_SUBSECTION (Subsection);
  3980. Subsection->UnusedPtes = j - Subsection->PtesInSubsection;
  3981. }
  3982. else {
  3983. Subsection->NumberOfFullSectors =
  3984. Subsection->PtesInSubsection << (PAGE_SHIFT - MM4K_SHIFT);
  3985. MI_CHECK_SUBSECTION (Subsection);
  3986. }
  3987. FileOffset += (((UINT64)Subsection->PtesInSubsection) <<
  3988. (PAGE_SHIFT - MM4K_SHIFT));
  3989. Subsection = (PMSUBSECTION) Subsection->NextSubsection;
  3990. } while (Subsection != NULL);
  3991. if (XIPConfigured == TRUE) {
  3992. Status = XIPLocatePages (File, &PhysicalAddress);
  3993. if (NT_SUCCESS(Status)) {
  3994. PageFrameNumber = (PFN_NUMBER) (PhysicalAddress.QuadPart >> PAGE_SHIFT);
  3995. //
  3996. // Allocate a large control area (so the starting frame number
  3997. // can be saved) and repoint all the created subsections to it.
  3998. //
  3999. LargeControlArea = ExAllocatePoolWithTag (NonPagedPool,
  4000. (ULONG)(sizeof(LARGE_CONTROL_AREA) +
  4001. sizeof(MSUBSECTION)),
  4002. MMCONTROL);
  4003. if (LargeControlArea != NULL) {
  4004. *(PCONTROL_AREA) LargeControlArea = *ControlArea;
  4005. if (MiMakeControlAreaRom (File, LargeControlArea, PageFrameNumber) == TRUE) {
  4006. LargeExtendedSubsection = (PMSUBSECTION)(LargeControlArea + 1);
  4007. ExtendedSubsection = (PMSUBSECTION)(ControlArea + 1);
  4008. *LargeExtendedSubsection = *ExtendedSubsection;
  4009. LargeExtendedSubsection->ControlArea = (PCONTROL_AREA) LargeControlArea;
  4010. //
  4011. // Only the first subsection needed to be directly modified
  4012. // as above because it is allocated in a single chunk with
  4013. // the control area. Any additional subsections below
  4014. // just need their control area pointers updated.
  4015. //
  4016. ASSERT (NumberOfNewSubsections >= 1);
  4017. j = NumberOfNewSubsections - 1;
  4018. while (j != 0) {
  4019. ExtendedSubsection = (PMSUBSECTION) ExtendedSubsection->NextSubsection;
  4020. ExtendedSubsection->ControlArea = (PCONTROL_AREA) LargeControlArea;
  4021. j -= 1;
  4022. }
  4023. NewSegment->ControlArea = (PCONTROL_AREA) LargeControlArea;
  4024. }
  4025. else {
  4026. ExFreePool (LargeControlArea);
  4027. }
  4028. }
  4029. }
  4030. }
  4031. return STATUS_SUCCESS;
  4032. }
  4033. NTSTATUS
  4034. MiCreatePagingFileMap (
  4035. OUT PSEGMENT *Segment,
  4036. IN PUINT64 MaximumSize,
  4037. IN ULONG ProtectionMask,
  4038. IN ULONG AllocationAttributes
  4039. )
  4040. /*++
  4041. Routine Description:
  4042. This function creates the necessary structures to allow the mapping
  4043. of a paging file.
  4044. Arguments:
  4045. Segment - Returns the segment object.
  4046. MaximumSize - Supplies the maximum size for the mapping.
  4047. ProtectionMask - Supplies the initial page protection.
  4048. AllocationAttributes - Supplies the allocation attributes for the
  4049. mapping.
  4050. Return Value:
  4051. NTSTATUS.
  4052. --*/
  4053. {
  4054. UINT64 MaximumFileSize;
  4055. PFN_NUMBER NumberOfPtes;
  4056. SIZE_T SizeOfSegment;
  4057. PCONTROL_AREA ControlArea;
  4058. PSEGMENT NewSegment;
  4059. PMMPTE PointerPte;
  4060. PSUBSECTION Subsection;
  4061. MMPTE TempPte;
  4062. PAGED_CODE();
  4063. //*******************************************************************
  4064. // Create a section backed by the paging file.
  4065. //*******************************************************************
  4066. if (*MaximumSize == 0) {
  4067. return STATUS_INVALID_PARAMETER_4;
  4068. }
  4069. //
  4070. // Limit page file backed sections to the amount of pool that could
  4071. // possibly be allocated to hold the prototype PTEs. Note this may
  4072. // be larger than the size of any *single* pagefile.
  4073. //
  4074. #if defined (_WIN64)
  4075. //
  4076. // Limit the maximum size to the number of PTEs that can be stored in
  4077. // the NonExtendedPtes field in the segment so that segment deletion
  4078. // which uses this will use the proper value.
  4079. //
  4080. MaximumFileSize = ((UINT64)1 << (32 + PAGE_SHIFT)) - sizeof (ULONG_PTR) - sizeof(SEGMENT);
  4081. #else
  4082. MaximumFileSize = MAXULONG_PTR - sizeof(SEGMENT);
  4083. #endif
  4084. MaximumFileSize /= sizeof(MMPTE);
  4085. MaximumFileSize <<= PAGE_SHIFT;
  4086. if (*MaximumSize > MaximumFileSize) {
  4087. return STATUS_SECTION_TOO_BIG;
  4088. }
  4089. //
  4090. // Create the segment object.
  4091. //
  4092. // Calculate the number of prototype PTEs to build for this segment.
  4093. //
  4094. NumberOfPtes = (PFN_NUMBER) ((*MaximumSize + PAGE_SIZE - 1) >> PAGE_SHIFT);
  4095. if (AllocationAttributes & SEC_COMMIT) {
  4096. //
  4097. // Commit the pages for the section.
  4098. //
  4099. ASSERT (ProtectionMask != 0);
  4100. if (MiChargeCommitment (NumberOfPtes, NULL) == FALSE) {
  4101. return STATUS_COMMITMENT_LIMIT;
  4102. }
  4103. }
  4104. SizeOfSegment = sizeof(SEGMENT) + sizeof(MMPTE) * (NumberOfPtes - 1);
  4105. NewSegment = ExAllocatePoolWithTag (PagedPool | POOL_MM_ALLOCATION,
  4106. SizeOfSegment,
  4107. MMSECT);
  4108. if (NewSegment == NULL) {
  4109. //
  4110. // The requested pool could not be allocated.
  4111. //
  4112. if (AllocationAttributes & SEC_COMMIT) {
  4113. MiReturnCommitment (NumberOfPtes);
  4114. }
  4115. return STATUS_INSUFFICIENT_RESOURCES;
  4116. }
  4117. *Segment = NewSegment;
  4118. ControlArea = ExAllocatePoolWithTag (NonPagedPool,
  4119. (ULONG)sizeof(CONTROL_AREA) +
  4120. (ULONG)sizeof(SUBSECTION),
  4121. MMCONTROL);
  4122. if (ControlArea == NULL) {
  4123. //
  4124. // The requested pool could not be allocated.
  4125. //
  4126. ExFreePool (NewSegment);
  4127. if (AllocationAttributes & SEC_COMMIT) {
  4128. MiReturnCommitment (NumberOfPtes);
  4129. }
  4130. return STATUS_INSUFFICIENT_RESOURCES;
  4131. }
  4132. //
  4133. // Zero control area and first subsection.
  4134. //
  4135. RtlZeroMemory (ControlArea, sizeof(CONTROL_AREA) + sizeof(SUBSECTION));
  4136. ControlArea->Segment = NewSegment;
  4137. ControlArea->NumberOfSectionReferences = 1;
  4138. ControlArea->NumberOfUserReferences = 1;
  4139. if (AllocationAttributes & SEC_BASED) {
  4140. ControlArea->u.Flags.Based = 1;
  4141. }
  4142. if (AllocationAttributes & SEC_RESERVE) {
  4143. ControlArea->u.Flags.Reserve = 1;
  4144. }
  4145. if (AllocationAttributes & SEC_COMMIT) {
  4146. ControlArea->u.Flags.Commit = 1;
  4147. }
  4148. Subsection = (PSUBSECTION)(ControlArea + 1);
  4149. Subsection->ControlArea = ControlArea;
  4150. Subsection->PtesInSubsection = (ULONG)NumberOfPtes;
  4151. Subsection->u.SubsectionFlags.Protection = ProtectionMask;
  4152. //
  4153. // Align the prototype PTEs on the proper boundary.
  4154. //
  4155. PointerPte = &NewSegment->ThePtes[0];
  4156. //
  4157. // Zero the segment header.
  4158. //
  4159. RtlZeroMemory (NewSegment, sizeof(SEGMENT));
  4160. NewSegment->PrototypePte = &NewSegment->ThePtes[0];
  4161. NewSegment->ControlArea = ControlArea;
  4162. //
  4163. // Record the process that created this segment for the performance
  4164. // analysis tools.
  4165. //
  4166. NewSegment->u1.CreatingProcess = PsGetCurrentProcess ();
  4167. NewSegment->SizeOfSegment = (UINT64)NumberOfPtes * PAGE_SIZE;
  4168. NewSegment->TotalNumberOfPtes = (ULONG)NumberOfPtes;
  4169. NewSegment->NonExtendedPtes = (ULONG)NumberOfPtes;
  4170. PointerPte = NewSegment->PrototypePte;
  4171. Subsection->SubsectionBase = PointerPte;
  4172. TempPte = ZeroPte;
  4173. if (AllocationAttributes & SEC_COMMIT) {
  4174. TempPte.u.Soft.Protection = ProtectionMask;
  4175. //
  4176. // Record commitment charging.
  4177. //
  4178. MM_TRACK_COMMIT (MM_DBG_COMMIT_PAGEFILE_BACKED_SHMEM, NumberOfPtes);
  4179. NewSegment->NumberOfCommittedPages = NumberOfPtes;
  4180. InterlockedExchangeAddSizeT (&MmSharedCommit, NumberOfPtes);
  4181. }
  4182. NewSegment->SegmentPteTemplate.u.Soft.Protection = ProtectionMask;
  4183. //
  4184. // Set all the prototype PTEs to either no access or demand zero
  4185. // depending on the commit flag.
  4186. //
  4187. MiFillMemoryPte (PointerPte, NumberOfPtes, TempPte.u.Long);
  4188. return STATUS_SUCCESS;
  4189. }
  4190. NTSTATUS
  4191. NtOpenSection (
  4192. OUT PHANDLE SectionHandle,
  4193. IN ACCESS_MASK DesiredAccess,
  4194. IN POBJECT_ATTRIBUTES ObjectAttributes
  4195. )
  4196. /*++
  4197. Routine Description:
  4198. This function opens a handle to a section object with the specified
  4199. desired access.
  4200. Arguments:
  4201. Sectionhandle - Supplies a pointer to a variable that will
  4202. receive the section object handle value.
  4203. DesiredAccess - Supplies the desired types of access for the
  4204. section.
  4205. DesiredAccess Flags
  4206. EXECUTE - Execute access to the section is desired.
  4207. READ - Read access to the section is desired.
  4208. WRITE - Write access to the section is desired.
  4209. ObjectAttributes - Supplies a pointer to an object attributes structure.
  4210. Return Value:
  4211. NTSTATUS.
  4212. --*/
  4213. {
  4214. HANDLE Handle;
  4215. KPROCESSOR_MODE PreviousMode;
  4216. NTSTATUS Status;
  4217. PAGED_CODE();
  4218. //
  4219. // Get previous processor mode and probe output arguments if necessary.
  4220. //
  4221. PreviousMode = KeGetPreviousMode();
  4222. if (PreviousMode != KernelMode) {
  4223. try {
  4224. ProbeForWriteHandle(SectionHandle);
  4225. } except (EXCEPTION_EXECUTE_HANDLER) {
  4226. return GetExceptionCode();
  4227. }
  4228. }
  4229. //
  4230. // Open handle to the section object with the specified desired
  4231. // access.
  4232. //
  4233. Status = ObOpenObjectByName (ObjectAttributes,
  4234. MmSectionObjectType,
  4235. PreviousMode,
  4236. NULL,
  4237. DesiredAccess,
  4238. NULL,
  4239. &Handle);
  4240. try {
  4241. *SectionHandle = Handle;
  4242. } except (EXCEPTION_EXECUTE_HANDLER) {
  4243. return Status;
  4244. }
  4245. return Status;
  4246. }
  4247. CCHAR
  4248. MiGetImageProtection (
  4249. IN ULONG SectionCharacteristics
  4250. )
  4251. /*++
  4252. Routine Description:
  4253. This function takes a section characteristic mask from the
  4254. image and converts it to an PTE protection mask.
  4255. Arguments:
  4256. SectionCharacteristics - Supplies the characteristics mask from the
  4257. image.
  4258. Return Value:
  4259. Returns the protection mask for the PTE.
  4260. --*/
  4261. {
  4262. ULONG Index;
  4263. PAGED_CODE();
  4264. Index = 0;
  4265. if (SectionCharacteristics & IMAGE_SCN_MEM_EXECUTE) {
  4266. Index |= 1;
  4267. }
  4268. if (SectionCharacteristics & IMAGE_SCN_MEM_READ) {
  4269. Index |= 2;
  4270. }
  4271. if (SectionCharacteristics & IMAGE_SCN_MEM_WRITE) {
  4272. Index |= 4;
  4273. }
  4274. if (SectionCharacteristics & IMAGE_SCN_MEM_SHARED) {
  4275. Index |= 8;
  4276. }
  4277. return MmImageProtectionArray[Index];
  4278. }
  4279. PFN_NUMBER
  4280. MiGetPageForHeader (
  4281. LOGICAL ZeroPage
  4282. )
  4283. /*++
  4284. Routine Description:
  4285. This non-pagable function acquires the PFN lock, removes
  4286. a page and updates the PFN database as though the page was
  4287. ready to be deleted if the reference count is decremented.
  4288. Arguments:
  4289. ZeroPage - Supplies TRUE if the caller requires a zero-filled page.
  4290. Return Value:
  4291. Returns the physical page frame number.
  4292. --*/
  4293. {
  4294. KIRQL OldIrql;
  4295. PFN_NUMBER PageFrameNumber;
  4296. PMMPFN Pfn1;
  4297. PEPROCESS Process;
  4298. ULONG PageColor;
  4299. Process = PsGetCurrentProcess ();
  4300. PageColor = MI_PAGE_COLOR_VA_PROCESS ((PVOID)X64K,
  4301. &Process->NextPageColor);
  4302. //
  4303. // Lock the PFN database and get a page.
  4304. //
  4305. LOCK_PFN (OldIrql);
  4306. if (MmAvailablePages < MM_HIGH_LIMIT) {
  4307. MiEnsureAvailablePageOrWait (NULL, NULL, OldIrql);
  4308. }
  4309. //
  4310. // Remove page for 64k alignment.
  4311. //
  4312. if (ZeroPage) {
  4313. PageFrameNumber = MiRemoveZeroPage (PageColor);
  4314. }
  4315. else {
  4316. PageFrameNumber = MiRemoveAnyPage (PageColor);
  4317. }
  4318. //
  4319. // Increment the reference count for the page so the
  4320. // paging I/O will work, and so this page cannot be stolen from us.
  4321. //
  4322. Pfn1 = MI_PFN_ELEMENT (PageFrameNumber);
  4323. Pfn1->u3.e2.ReferenceCount += 1;
  4324. //
  4325. // Don't need the PFN lock for the fields below...
  4326. //
  4327. UNLOCK_PFN (OldIrql);
  4328. ASSERT (Pfn1->u1.Flink == 0);
  4329. ASSERT (Pfn1->u2.Blink == 0);
  4330. Pfn1->OriginalPte = ZeroPte;
  4331. Pfn1->PteAddress = (PVOID) (ULONG_PTR)X64K;
  4332. MI_SET_PFN_DELETED (Pfn1);
  4333. return PageFrameNumber;
  4334. }
  4335. VOID
  4336. MiUpdateImageHeaderPage (
  4337. IN PMMPTE PointerPte,
  4338. IN PFN_NUMBER PageFrameNumber,
  4339. IN PCONTROL_AREA ControlArea,
  4340. IN LOGICAL MarkModified
  4341. )
  4342. /*++
  4343. Routine Description:
  4344. This non-pagable function acquires the PFN lock, and
  4345. turns the specified prototype PTE into a transition PTE
  4346. referring to the specified physical page. It then
  4347. decrements the reference count causing the page to
  4348. be placed on the standby or modified list.
  4349. Arguments:
  4350. PointerPte - Supplies the PTE to set into the transition state.
  4351. PageFrameNumber - Supplies the physical page.
  4352. ControlArea - Supplies the control area for the prototype PTEs.
  4353. MarkModified - Supplies TRUE if the PFN should be marked modified (this
  4354. will give it pagefile-backing on trim vs filesystem backing).
  4355. Return Value:
  4356. None.
  4357. --*/
  4358. {
  4359. PMMPTE PointerPde;
  4360. PMMPFN Pfn1;
  4361. KIRQL OldIrql;
  4362. Pfn1 = MI_PFN_ELEMENT (PageFrameNumber);
  4363. PointerPde = MiGetPteAddress (PointerPte);
  4364. LOCK_PFN (OldIrql);
  4365. if (PointerPde->u.Hard.Valid == 0) {
  4366. MiMakeSystemAddressValidPfn (PointerPte, OldIrql);
  4367. }
  4368. MiInitializeTransitionPfn (PageFrameNumber, PointerPte);
  4369. if (MarkModified == TRUE) {
  4370. MI_SET_MODIFIED (Pfn1, 1, 0x19);
  4371. }
  4372. if (Pfn1->OriginalPte.u.Soft.Prototype == 1) {
  4373. ControlArea->NumberOfPfnReferences += 1;
  4374. }
  4375. //
  4376. // Add the page to the standby list.
  4377. //
  4378. MiDecrementReferenceCount (Pfn1, PageFrameNumber);
  4379. UNLOCK_PFN (OldIrql);
  4380. return;
  4381. }
  4382. VOID
  4383. MiRemoveImageHeaderPage (
  4384. IN PFN_NUMBER PageFrameNumber
  4385. )
  4386. /*++
  4387. Routine Description:
  4388. This non-pagable function acquires the PFN lock, and decrements
  4389. the reference count thereby causing the physical page to
  4390. be deleted.
  4391. Arguments:
  4392. PageFrameNumber - Supplies the PFN to decrement.
  4393. Return Value:
  4394. None.
  4395. --*/
  4396. {
  4397. PMMPFN Pfn1;
  4398. KIRQL OldIrql;
  4399. Pfn1 = MI_PFN_ELEMENT (PageFrameNumber);
  4400. LOCK_PFN (OldIrql);
  4401. MiDecrementReferenceCount (Pfn1, PageFrameNumber);
  4402. UNLOCK_PFN (OldIrql);
  4403. return;
  4404. }
  4405. PCONTROL_AREA
  4406. MiFindImageSectionObject (
  4407. IN PFILE_OBJECT File,
  4408. OUT PLOGICAL GlobalNeeded
  4409. )
  4410. /*++
  4411. Routine Description:
  4412. This function searches the control area chains (if any) for an existing
  4413. cache of the specified image file. For non-global control areas, there is
  4414. no chain and the control area is shared for all callers and sessions.
  4415. Likewise for systemwide global control areas.
  4416. However, for global PER-SESSION control areas, we must do the walk.
  4417. Arguments:
  4418. File - Supplies the file object for the image file.
  4419. GlobalNeeded - Supplies a pointer to store whether a global control area is
  4420. required as a placeholder. This can only be set when there
  4421. is already some global control area in the list - ie: our
  4422. caller should only rely on this when this function returns
  4423. NULL so the caller knows what kind of control area to
  4424. insert.
  4425. Return Value:
  4426. Returns the matching control area or NULL if one does not exist.
  4427. Environment:
  4428. Must be holding the PFN lock.
  4429. --*/
  4430. {
  4431. PCONTROL_AREA ControlArea;
  4432. PLARGE_CONTROL_AREA LargeControlArea;
  4433. PLIST_ENTRY Head, Next;
  4434. ULONG SessionId;
  4435. MM_PFN_LOCK_ASSERT();
  4436. *GlobalNeeded = FALSE;
  4437. //
  4438. // Get first (if any) control area pointer.
  4439. //
  4440. ControlArea = (PCONTROL_AREA)(File->SectionObjectPointer->ImageSectionObject);
  4441. //
  4442. // If no control area, or the control area is not session global,
  4443. // then our job is easy. Note, however, that they each require different
  4444. // return values as they represent different states.
  4445. //
  4446. if (ControlArea == NULL) {
  4447. return NULL;
  4448. }
  4449. if (ControlArea->u.Flags.GlobalOnlyPerSession == 0) {
  4450. return ControlArea;
  4451. }
  4452. LargeControlArea = (PLARGE_CONTROL_AREA) ControlArea;
  4453. //
  4454. // Get the current session ID and search for a matching control area.
  4455. //
  4456. SessionId = MmGetSessionId (PsGetCurrentProcess());
  4457. if (LargeControlArea->SessionId == SessionId) {
  4458. return (PCONTROL_AREA) LargeControlArea;
  4459. }
  4460. //
  4461. // Must search the control area list for a matching session ID.
  4462. //
  4463. Head = &LargeControlArea->UserGlobalList;
  4464. for (Next = Head->Flink; Next != Head; Next = Next->Flink) {
  4465. LargeControlArea = CONTAINING_RECORD (Next, LARGE_CONTROL_AREA, UserGlobalList);
  4466. ASSERT (LargeControlArea->u.Flags.GlobalOnlyPerSession == 1);
  4467. if (LargeControlArea->SessionId == SessionId) {
  4468. return (PCONTROL_AREA) LargeControlArea;
  4469. }
  4470. }
  4471. //
  4472. // No match, so tell our caller to create a new global control area.
  4473. //
  4474. *GlobalNeeded = TRUE;
  4475. return NULL;
  4476. }
  4477. VOID
  4478. MiInsertImageSectionObject(
  4479. IN PFILE_OBJECT File,
  4480. IN PCONTROL_AREA InputControlArea
  4481. )
  4482. /*++
  4483. Routine Description:
  4484. This function inserts the control area into the file's section object
  4485. pointers. For non-global control areas, there is no chain and the
  4486. control area is shared for all callers and sessions.
  4487. Likewise for systemwide global control areas.
  4488. However, for global PER-SESSION control areas, we must do a list insertion.
  4489. Arguments:
  4490. File - Supplies the file object for the image file.
  4491. InputControlArea - Supplies the control area to insert.
  4492. Return Value:
  4493. None.
  4494. Environment:
  4495. Must be holding the PFN lock.
  4496. --*/
  4497. {
  4498. PLIST_ENTRY Head;
  4499. PLARGE_CONTROL_AREA ControlArea;
  4500. PLARGE_CONTROL_AREA FirstControlArea;
  4501. #if DBG
  4502. PLIST_ENTRY Next;
  4503. PLARGE_CONTROL_AREA NextControlArea;
  4504. #endif
  4505. MM_PFN_LOCK_ASSERT();
  4506. ControlArea = (PLARGE_CONTROL_AREA) InputControlArea;
  4507. //
  4508. // If this is not a global-per-session control area or just a placeholder
  4509. // control area (with no chain already in place) then just put it in.
  4510. //
  4511. FirstControlArea = (PLARGE_CONTROL_AREA)(File->SectionObjectPointer->ImageSectionObject);
  4512. if (FirstControlArea == NULL) {
  4513. if (ControlArea->u.Flags.GlobalOnlyPerSession == 0) {
  4514. File->SectionObjectPointer->ImageSectionObject = (PVOID)ControlArea;
  4515. return;
  4516. }
  4517. }
  4518. //
  4519. // A per-session control area needs to be inserted...
  4520. //
  4521. ASSERT (ControlArea->u.Flags.GlobalOnlyPerSession == 1);
  4522. ControlArea->SessionId = MmGetSessionId (PsGetCurrentProcess());
  4523. //
  4524. // If the control area list is empty, just initialize links for this entry.
  4525. //
  4526. if (File->SectionObjectPointer->ImageSectionObject == NULL) {
  4527. InitializeListHead (&ControlArea->UserGlobalList);
  4528. }
  4529. else {
  4530. //
  4531. // Insert new entry before the current first entry. The control area
  4532. // must be in the midst of creation/deletion or have a valid session
  4533. // ID to be inserted.
  4534. //
  4535. ASSERT (ControlArea->u.Flags.BeingDeleted ||
  4536. ControlArea->u.Flags.BeingCreated ||
  4537. ControlArea->SessionId != (ULONG)-1);
  4538. FirstControlArea = (PLARGE_CONTROL_AREA)(File->SectionObjectPointer->ImageSectionObject);
  4539. Head = &FirstControlArea->UserGlobalList;
  4540. #if DBG
  4541. //
  4542. // Ensure no duplicate session IDs exist in the list.
  4543. //
  4544. for (Next = Head->Flink; Next != Head; Next = Next->Flink) {
  4545. NextControlArea = CONTAINING_RECORD (Next, LARGE_CONTROL_AREA, UserGlobalList);
  4546. ASSERT (NextControlArea->SessionId != (ULONG)-1 &&
  4547. NextControlArea->SessionId != ControlArea->SessionId);
  4548. }
  4549. #endif
  4550. InsertTailList (Head, &ControlArea->UserGlobalList);
  4551. }
  4552. //
  4553. // Update first control area pointer.
  4554. //
  4555. File->SectionObjectPointer->ImageSectionObject = (PVOID) ControlArea;
  4556. }
  4557. VOID
  4558. MiRemoveImageSectionObject(
  4559. IN PFILE_OBJECT File,
  4560. IN PCONTROL_AREA InputControlArea
  4561. )
  4562. /*++
  4563. Routine Description:
  4564. This function searches the control area chains (if any) for an existing
  4565. cache of the specified image file. For non-global control areas, there is
  4566. no chain and the control area is shared for all callers and sessions.
  4567. Likewise for systemwide global control areas.
  4568. However, for global PER-SESSION control areas, we must do the walk.
  4569. Upon finding the specified control area, we unlink it.
  4570. Arguments:
  4571. File - Supplies the file object for the image file.
  4572. InputControlArea - Supplies the control area to remove.
  4573. Return Value:
  4574. None.
  4575. Environment:
  4576. Must be holding the PFN lock.
  4577. --*/
  4578. {
  4579. #if DBG
  4580. PLIST_ENTRY Head;
  4581. #endif
  4582. PLIST_ENTRY Next;
  4583. PLARGE_CONTROL_AREA ControlArea;
  4584. PLARGE_CONTROL_AREA FirstControlArea;
  4585. PLARGE_CONTROL_AREA NextControlArea;
  4586. MM_PFN_LOCK_ASSERT();
  4587. ControlArea = (PLARGE_CONTROL_AREA) InputControlArea;
  4588. FirstControlArea = (PLARGE_CONTROL_AREA)(File->SectionObjectPointer->ImageSectionObject);
  4589. //
  4590. // Get a pointer to the first control area. If this is not a
  4591. // global-per-session control area, then there is no list, so we're done.
  4592. //
  4593. if (FirstControlArea->u.Flags.GlobalOnlyPerSession == 0) {
  4594. ASSERT (ControlArea->u.Flags.GlobalOnlyPerSession == 0);
  4595. File->SectionObjectPointer->ImageSectionObject = NULL;
  4596. return;
  4597. }
  4598. //
  4599. // A list may exist. Walk it as necessary and delete the requested entry.
  4600. //
  4601. if (FirstControlArea == ControlArea) {
  4602. //
  4603. // The first entry is the one to remove. If it is the only entry
  4604. // in the list, then the new first entry pointer will be NULL.
  4605. // Otherwise, get a pointer to the next entry and unlink the current.
  4606. //
  4607. if (IsListEmpty (&FirstControlArea->UserGlobalList)) {
  4608. NextControlArea = NULL;
  4609. }
  4610. else {
  4611. Next = FirstControlArea->UserGlobalList.Flink;
  4612. RemoveEntryList (&FirstControlArea->UserGlobalList);
  4613. NextControlArea = CONTAINING_RECORD (Next,
  4614. LARGE_CONTROL_AREA,
  4615. UserGlobalList);
  4616. ASSERT (NextControlArea->u.Flags.GlobalOnlyPerSession == 1);
  4617. }
  4618. File->SectionObjectPointer->ImageSectionObject = (PVOID)NextControlArea;
  4619. return;
  4620. }
  4621. //
  4622. // Remove the entry, note that the ImageSectionObject need not be updated
  4623. // as the entry is not at the head.
  4624. //
  4625. #if DBG
  4626. Head = &FirstControlArea->UserGlobalList;
  4627. for (Next = Head->Flink; Next != Head; Next = Next->Flink) {
  4628. NextControlArea = CONTAINING_RECORD (Next,
  4629. LARGE_CONTROL_AREA,
  4630. UserGlobalList);
  4631. ASSERT (NextControlArea->u.Flags.GlobalOnlyPerSession == 1);
  4632. if (NextControlArea == ControlArea) {
  4633. break;
  4634. }
  4635. }
  4636. ASSERT (Next != Head);
  4637. #endif
  4638. RemoveEntryList (&ControlArea->UserGlobalList);
  4639. }
  4640. LOGICAL
  4641. MiFlushDataSection (
  4642. IN PFILE_OBJECT File
  4643. )
  4644. /*++
  4645. Routine Description:
  4646. This routine flushes the data section if there is one.
  4647. Arguments:
  4648. File - Supplies the file object.
  4649. Return Value:
  4650. TRUE if there is a data section that may be in use, FALSE if not.
  4651. Environment:
  4652. Kernel mode, APC_LEVEL and below.
  4653. --*/
  4654. {
  4655. KIRQL OldIrql;
  4656. IO_STATUS_BLOCK IoStatus;
  4657. PCONTROL_AREA ControlArea;
  4658. LOGICAL DataInUse;
  4659. DataInUse = FALSE;
  4660. LOCK_PFN (OldIrql);
  4661. ControlArea = (PCONTROL_AREA) File->SectionObjectPointer->DataSectionObject;
  4662. if (ControlArea) {
  4663. if ((ControlArea->NumberOfSectionReferences != 0) ||
  4664. (ControlArea->NumberOfMappedViews != 0)) {
  4665. DataInUse = TRUE;
  4666. }
  4667. if (ControlArea->NumberOfSystemCacheViews) {
  4668. UNLOCK_PFN (OldIrql);
  4669. CcFlushCache (File->SectionObjectPointer,
  4670. NULL,
  4671. 0,
  4672. &IoStatus);
  4673. }
  4674. else {
  4675. UNLOCK_PFN (OldIrql);
  4676. MmFlushSection (File->SectionObjectPointer,
  4677. NULL,
  4678. 0,
  4679. &IoStatus,
  4680. TRUE);
  4681. }
  4682. }
  4683. else {
  4684. UNLOCK_PFN (OldIrql);
  4685. }
  4686. return DataInUse;
  4687. }
  4688. PVOID
  4689. MiCopyHeaderIfResident (
  4690. IN PFILE_OBJECT File,
  4691. IN PFN_NUMBER ImagePageFrameNumber
  4692. )
  4693. /*++
  4694. Routine Description:
  4695. This routine copies the image header from the data section if there is
  4696. one and the page is already resident or in transition.
  4697. Arguments:
  4698. File - Supplies the file object.
  4699. ImagePageFrameNumber - Supplies the image frame to copy the data into.
  4700. Return Value:
  4701. Virtual address of the image page frame number if successful, NULL if not.
  4702. Environment:
  4703. Kernel mode, APC_LEVEL and below.
  4704. --*/
  4705. {
  4706. PMMPFN Pfn1;
  4707. PVOID DataPage;
  4708. PVOID ImagePage;
  4709. KIRQL OldIrql;
  4710. PCONTROL_AREA ControlArea;
  4711. PMMPTE PointerPte;
  4712. MMPTE PteContents;
  4713. PFN_NUMBER PageFrameIndex;
  4714. PEPROCESS Process;
  4715. PSUBSECTION Subsection;
  4716. PSECTION_OBJECT_POINTERS SectionObjectPointer;
  4717. //
  4718. // Take a quick (safely unsynchronized) look to see whether to bother
  4719. // mapping the image header page at all - if there's no data section
  4720. // object, then skip it and just return.
  4721. //
  4722. SectionObjectPointer = File->SectionObjectPointer;
  4723. if (SectionObjectPointer == NULL) {
  4724. return NULL;
  4725. }
  4726. ControlArea = (PCONTROL_AREA) SectionObjectPointer->DataSectionObject;
  4727. if (ControlArea == NULL) {
  4728. return NULL;
  4729. }
  4730. //
  4731. // There's a data section, so map the target page.
  4732. //
  4733. ImagePage = MiMapImageHeaderInHyperSpace (ImagePageFrameNumber);
  4734. LOCK_PFN (OldIrql);
  4735. //
  4736. // Now that we are synchronized via the PFN lock, take a safe look.
  4737. //
  4738. SectionObjectPointer = File->SectionObjectPointer;
  4739. if (SectionObjectPointer == NULL) {
  4740. UNLOCK_PFN (OldIrql);
  4741. MiUnmapImageHeaderInHyperSpace ();
  4742. return NULL;
  4743. }
  4744. ControlArea = (PCONTROL_AREA) SectionObjectPointer->DataSectionObject;
  4745. if (ControlArea == NULL) {
  4746. UNLOCK_PFN (OldIrql);
  4747. MiUnmapImageHeaderInHyperSpace ();
  4748. return NULL;
  4749. }
  4750. if ((ControlArea->u.Flags.BeingCreated) ||
  4751. (ControlArea->u.Flags.BeingDeleted)) {
  4752. UNLOCK_PFN (OldIrql);
  4753. MiUnmapImageHeaderInHyperSpace ();
  4754. return NULL;
  4755. }
  4756. if (ControlArea->u.Flags.Rom == 0) {
  4757. Subsection = (PSUBSECTION) (ControlArea + 1);
  4758. }
  4759. else {
  4760. Subsection = (PSUBSECTION)((PLARGE_CONTROL_AREA)ControlArea + 1);
  4761. }
  4762. //
  4763. // If the prototype PTEs have been tossed (or never created) then we
  4764. // don't have any data to copy.
  4765. //
  4766. PointerPte = Subsection->SubsectionBase;
  4767. if (PointerPte == NULL) {
  4768. UNLOCK_PFN (OldIrql);
  4769. MiUnmapImageHeaderInHyperSpace ();
  4770. return NULL;
  4771. }
  4772. if (MiGetPteAddress (PointerPte)->u.Hard.Valid == 0) {
  4773. //
  4774. // We have no reference to the data section so if we can't do this
  4775. // without relinquishing the PFN lock, then don't bother.
  4776. // ie: the entire control area and everything can be freed
  4777. // while a call to MiMakeSystemAddressValidPfn releases the lock.
  4778. //
  4779. UNLOCK_PFN (OldIrql);
  4780. MiUnmapImageHeaderInHyperSpace ();
  4781. return NULL;
  4782. }
  4783. PteContents = *PointerPte;
  4784. if ((PteContents.u.Hard.Valid == 1) ||
  4785. ((PteContents.u.Soft.Prototype == 0) &&
  4786. (PteContents.u.Soft.Transition == 1))) {
  4787. if (PteContents.u.Hard.Valid == 1) {
  4788. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (&PteContents);
  4789. }
  4790. else {
  4791. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&PteContents);
  4792. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  4793. if (Pfn1->u3.e1.ReadInProgress != 0) {
  4794. UNLOCK_PFN (OldIrql);
  4795. MiUnmapImageHeaderInHyperSpace ();
  4796. return NULL;
  4797. }
  4798. }
  4799. Process = PsGetCurrentProcess ();
  4800. DataPage = MiMapPageInHyperSpaceAtDpc (Process, PageFrameIndex);
  4801. RtlCopyMemory (ImagePage, DataPage, PAGE_SIZE);
  4802. MiUnmapPageInHyperSpaceFromDpc (Process, DataPage);
  4803. UNLOCK_PFN (OldIrql);
  4804. return ImagePage;
  4805. }
  4806. //
  4807. // The data page is not resident, so return NULL and the caller will take
  4808. // the long way.
  4809. //
  4810. UNLOCK_PFN (OldIrql);
  4811. MiUnmapImageHeaderInHyperSpace ();
  4812. return NULL;
  4813. }