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.

1272 lines
36 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. sessload.c
  5. Abstract:
  6. This module contains the routines which implement the loading of
  7. session space drivers.
  8. Author:
  9. Landy Wang (landyw) 05-Dec-1997
  10. Revision History:
  11. --*/
  12. #include "mi.h"
  13. //
  14. // This tracks allocated group virtual addresses. The term SESSIONWIDE is used
  15. // to denote data that is the same across all sessions (as opposed to
  16. // per-session data which can vary from session to session).
  17. //
  18. // Since each driver loaded into a session space is linked and fixed up
  19. // against the system image, it must remain at the same virtual address
  20. // across the system regardless of the session.
  21. //
  22. // Access to these structures are generally guarded by the MmSystemLoadLock.
  23. //
  24. RTL_BITMAP MiSessionWideVaBitMap;
  25. ULONG MiSessionUserCollisions;
  26. //
  27. // External function references
  28. //
  29. ULONG
  30. MiSetProtectionOnTransitionPte (
  31. IN PMMPTE PointerPte,
  32. IN ULONG ProtectionMask
  33. );
  34. NTSTATUS
  35. MiSessionRemoveImage (
  36. IN PVOID BaseAddress
  37. );
  38. #ifdef ALLOC_PRAGMA
  39. #pragma alloc_text(INIT, MiSessionWideInitializeAddresses)
  40. #pragma alloc_text(PAGE, MiSessionWideReserveImageAddress)
  41. #pragma alloc_text(PAGE, MiRemoveImageSessionWide)
  42. #pragma alloc_text(PAGE, MiShareSessionImage)
  43. #pragma alloc_text(PAGE, MiSessionInsertImage)
  44. #pragma alloc_text(PAGE, MiSessionRemoveImage)
  45. #pragma alloc_text(PAGE, MiSessionLookupImage)
  46. #pragma alloc_text(PAGE, MiSessionUnloadAllImages)
  47. #endif
  48. LOGICAL
  49. MiMarkImageInSystem (
  50. IN PCONTROL_AREA ControlArea
  51. )
  52. /*++
  53. Routine Description:
  54. This routine marks the given image as mapped into system space.
  55. Arguments:
  56. ControlArea - Supplies the relevant control area.
  57. Return Value:
  58. TRUE on success, FALSE on failure.
  59. Environment:
  60. Kernel mode, APC_LEVEL and below, MmSystemLoadLock held.
  61. --*/
  62. {
  63. LOGICAL Status;
  64. KIRQL OldIrql;
  65. ASSERT (ControlArea->u.Flags.ImageMappedInSystemSpace == 0);
  66. Status = TRUE;
  67. //
  68. // Lock synchronization is not needed for our callers as they always hold
  69. // the system load mutant - but it is needed to modify this field in the
  70. // control area as other threads may be modifying other bits in the flags.
  71. //
  72. LOCK_PFN (OldIrql);
  73. //
  74. // Before handling the relocations for this image, ensure it
  75. // is not mapped in user space anywhere. Note we have 1 user
  76. // reference at this point, so any beyond that are someone
  77. // elses and force us to pagefile-back this image.
  78. //
  79. if (ControlArea->NumberOfUserReferences <= 1) {
  80. ControlArea->u.Flags.ImageMappedInSystemSpace = 1;
  81. //
  82. // This flag is set so when the image is removed from the loaded
  83. // module list, the control area is destroyed. This is required
  84. // because images mapped in session space inherit their PTE protections
  85. // from the shared prototype PTEs.
  86. //
  87. // Consider the following scenario :
  88. //
  89. // If image A is loaded at its based (preferred) address, and then
  90. // unloaded. Image B is not rebased properly and then loads at
  91. // image A's preferred address. Image A then reloads.
  92. //
  93. // Now image A cannot use the original prototype PTEs which enforce
  94. // readonly code, etc, because fixups will need to be done on it.
  95. //
  96. // Setting DeleteOnClose solves this problem by simply destroying
  97. // the entire control area on last unload.
  98. //
  99. ControlArea->u.Flags.DeleteOnClose = 1;
  100. }
  101. else {
  102. Status = FALSE;
  103. }
  104. UNLOCK_PFN (OldIrql);
  105. return Status;
  106. }
  107. NTSTATUS
  108. MiShareSessionImage (
  109. IN PVOID MappedBase,
  110. IN PSECTION Section
  111. )
  112. /*++
  113. Routine Description:
  114. This routine maps the given image into the current session space.
  115. This allows the image to be executed backed by the image file in the
  116. filesystem and allow code and read-only data to be shared.
  117. Arguments:
  118. MappedBase - Supplies the base address the image is to be mapped at.
  119. Section - Supplies a pointer to a section.
  120. Return Value:
  121. Returns STATUS_SUCCESS on success, various NTSTATUS codes on failure.
  122. Environment:
  123. Kernel mode, APC_LEVEL and below, MmSystemLoadLock held.
  124. --*/
  125. {
  126. PSUBSECTION Subsection;
  127. PCONTROL_AREA ControlArea;
  128. PFN_NUMBER NumberOfPtes;
  129. PMMPTE StartPte;
  130. #if DBG
  131. PMMPTE EndPte;
  132. #endif
  133. SIZE_T AllocationSize;
  134. NTSTATUS Status;
  135. SIZE_T CommittedPages;
  136. LOGICAL Relocated;
  137. PIMAGE_ENTRY_IN_SESSION DriverImage;
  138. PAGED_CODE();
  139. SYSLOAD_LOCK_OWNED_BY_ME ();
  140. ASSERT (MmIsAddressValid (MmSessionSpace) == TRUE);
  141. if (MappedBase != Section->Segment->BasedAddress) {
  142. Relocated = TRUE;
  143. }
  144. else {
  145. Relocated = FALSE;
  146. }
  147. ASSERT (BYTE_OFFSET (MappedBase) == 0);
  148. //
  149. // Check to see if a purge operation is in progress and if so, wait
  150. // for the purge to complete. In addition, up the count of mapped
  151. // views for this control area.
  152. //
  153. ControlArea = Section->Segment->ControlArea;
  154. if ((ControlArea->u.Flags.GlobalOnlyPerSession == 0) &&
  155. (ControlArea->u.Flags.Rom == 0)) {
  156. Subsection = (PSUBSECTION)(ControlArea + 1);
  157. }
  158. else {
  159. Subsection = (PSUBSECTION)((PLARGE_CONTROL_AREA)ControlArea + 1);
  160. }
  161. Status = MiCheckPurgeAndUpMapCount (ControlArea, FALSE);
  162. if (!NT_SUCCESS (Status)) {
  163. return Status;
  164. }
  165. NumberOfPtes = Section->Segment->TotalNumberOfPtes;
  166. AllocationSize = NumberOfPtes << PAGE_SHIFT;
  167. //
  168. // Calculate the PTE ranges and amount.
  169. //
  170. StartPte = MiGetPteAddress (MappedBase);
  171. //
  172. // The image commitment will be the same as the number of PTEs if the
  173. // image was not linked with native page alignment for the subsections.
  174. //
  175. // If it is linked native, then the commit will just be the number of
  176. // writable pages. Note for this case, if we need to relocate it, then
  177. // we need to charge for the full number of PTEs and bump the commit
  178. // charge in the segment so that the return on unload is correct also.
  179. //
  180. ASSERT (Section->Segment->u1.ImageCommitment != 0);
  181. ASSERT (Section->Segment->u1.ImageCommitment <= NumberOfPtes);
  182. if (Relocated == TRUE) {
  183. CommittedPages = NumberOfPtes;
  184. }
  185. else {
  186. CommittedPages = Section->Segment->u1.ImageCommitment;
  187. }
  188. if (MiChargeCommitment (CommittedPages, NULL) == FALSE) {
  189. MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_NO_COMMIT);
  190. //
  191. // Don't bother releasing the page tables or their commit here, another
  192. // load will happen shortly or the whole session will go away. On
  193. // session exit everything will be released automatically.
  194. //
  195. MiDereferenceControlArea (ControlArea);
  196. return STATUS_NO_MEMORY;
  197. }
  198. InterlockedExchangeAddSizeT (&MmSessionSpace->CommittedPages,
  199. CommittedPages);
  200. //
  201. // Make sure we have page tables for the PTE
  202. // entries we must fill in the session space structure.
  203. //
  204. Status = MiSessionCommitPageTables (MappedBase,
  205. (PVOID)((PCHAR)MappedBase + AllocationSize));
  206. if (!NT_SUCCESS(Status)) {
  207. InterlockedExchangeAddSizeT (&MmSessionSpace->CommittedPages,
  208. 0 - CommittedPages);
  209. MiDereferenceControlArea (ControlArea);
  210. MiReturnCommitment (CommittedPages);
  211. return STATUS_NO_MEMORY;
  212. }
  213. #if DBG
  214. EndPte = StartPte + NumberOfPtes;
  215. while (StartPte < EndPte) {
  216. ASSERT (StartPte->u.Long == 0);
  217. StartPte += 1;
  218. }
  219. StartPte = MiGetPteAddress (MappedBase);
  220. #endif
  221. //
  222. // If the image was linked with subsection alignment of >= PAGE_SIZE,
  223. // then all of the prototype PTEs were initialized to proper protections by
  224. // the initial section creation. The protections in these PTEs are used
  225. // to fill the actual PTEs as each address is faulted on.
  226. //
  227. // If the image has less than PAGE_SIZE section alignment, then
  228. // section creation uses a single subsection to map the entire file and
  229. // sets all the prototype PTEs to copy on write. For this case, the
  230. // appropriate permissions are set by the MiWriteProtectSystemImage below.
  231. //
  232. //
  233. // Initialize the PTEs to point at the prototype PTEs.
  234. //
  235. Status = MiAddMappedPtes (StartPte, NumberOfPtes, ControlArea);
  236. if (!NT_SUCCESS (Status)) {
  237. //
  238. // Regardless of whether the PTEs were mapped, leave the control area
  239. // marked as mapped in system space so user applications cannot map the
  240. // file as an image as clearly the intent is to run it as a driver.
  241. //
  242. InterlockedExchangeAddSizeT (&MmSessionSpace->CommittedPages,
  243. 0 - CommittedPages);
  244. MiDereferenceControlArea (ControlArea);
  245. MiReturnCommitment (CommittedPages);
  246. return Status;
  247. }
  248. MM_TRACK_COMMIT (MM_DBG_COMMIT_SESSION_SHARED_IMAGE, CommittedPages);
  249. MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_SYSMAPPED_PAGES_COMMITTED, (ULONG)CommittedPages);
  250. MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_SYSMAPPED_PAGES_ALLOC, (ULONG)NumberOfPtes);
  251. //
  252. // No session space image faults may be taken until these fields of the
  253. // image entry are initialized.
  254. //
  255. DriverImage = MiSessionLookupImage (MappedBase);
  256. ASSERT (DriverImage);
  257. DriverImage->LastAddress = (PVOID)((PCHAR)MappedBase + AllocationSize - 1);
  258. DriverImage->PrototypePtes = Subsection->SubsectionBase;
  259. //
  260. // The loaded module list section reference protects the image from
  261. // being purged while it's in use.
  262. //
  263. MiDereferenceControlArea (ControlArea);
  264. return STATUS_SUCCESS;
  265. }
  266. NTSTATUS
  267. MiSessionInsertImage (
  268. IN PVOID BaseAddress
  269. )
  270. /*++
  271. Routine Description:
  272. This routine allocates an image entry for the specified address in the
  273. current session space.
  274. Arguments:
  275. BaseAddress - Supplies the base address for the executable image.
  276. Return Value:
  277. STATUS_SUCCESS or various NTSTATUS error codes on failure.
  278. Environment:
  279. Kernel mode, APC_LEVEL and below, MmSystemLoadLock held.
  280. Note both the system load resource and the session working set
  281. mutex must be held to modify the list of images in this session.
  282. Either may be held to safely walk the list.
  283. --*/
  284. {
  285. PLIST_ENTRY NextEntry;
  286. PIMAGE_ENTRY_IN_SESSION Image;
  287. PIMAGE_ENTRY_IN_SESSION NewImage;
  288. PMMSUPPORT Ws;
  289. PAGED_CODE();
  290. SYSLOAD_LOCK_OWNED_BY_ME ();
  291. //
  292. // Create and initialize a new image entry prior to acquiring the session
  293. // space ws mutex. This is to reduce the amount of time the mutex is held.
  294. // If an existing entry is found this allocation is just discarded.
  295. //
  296. NewImage = ExAllocatePoolWithTag (NonPagedPool,
  297. sizeof(IMAGE_ENTRY_IN_SESSION),
  298. 'iHmM');
  299. if (NewImage == NULL) {
  300. MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_NO_NONPAGED_POOL);
  301. return STATUS_NO_MEMORY;
  302. }
  303. RtlZeroMemory (NewImage, sizeof(IMAGE_ENTRY_IN_SESSION));
  304. NewImage->Address = BaseAddress;
  305. NewImage->ImageCountInThisSession = 1;
  306. //
  307. // Check to see if the address is already loaded.
  308. //
  309. Ws = &MmSessionSpace->GlobalVirtualAddress->Vm;
  310. LOCK_WORKING_SET (Ws);
  311. NextEntry = MmSessionSpace->ImageList.Flink;
  312. while (NextEntry != &MmSessionSpace->ImageList) {
  313. Image = CONTAINING_RECORD (NextEntry, IMAGE_ENTRY_IN_SESSION, Link);
  314. if (Image->Address == BaseAddress) {
  315. Image->ImageCountInThisSession += 1;
  316. UNLOCK_WORKING_SET (Ws);
  317. ExFreePool (NewImage);
  318. return STATUS_ALREADY_COMMITTED;
  319. }
  320. NextEntry = NextEntry->Flink;
  321. }
  322. //
  323. // Insert the image entry into the session space structure.
  324. //
  325. InsertTailList (&MmSessionSpace->ImageList, &NewImage->Link);
  326. UNLOCK_WORKING_SET (Ws);
  327. return STATUS_SUCCESS;
  328. }
  329. NTSTATUS
  330. MiSessionRemoveImage (
  331. PVOID BaseAddr
  332. )
  333. /*++
  334. Routine Description:
  335. This routine removes the given image entry from the current session space.
  336. Arguments:
  337. BaseAddress - Supplies the base address for the executable image.
  338. Return Value:
  339. Returns STATUS_SUCCESS on success, STATUS_NOT_FOUND if the image is not
  340. in the current session space.
  341. Environment:
  342. Kernel mode, APC_LEVEL and below.
  343. Note both the system load resource and the session working set
  344. mutex must be held to modify the list of images in this session.
  345. Either may be held to safely walk the list.
  346. --*/
  347. {
  348. PLIST_ENTRY NextEntry;
  349. PIMAGE_ENTRY_IN_SESSION Image;
  350. PMMSUPPORT Ws;
  351. PAGED_CODE();
  352. SYSLOAD_LOCK_OWNED_BY_ME ();
  353. Ws = &MmSessionSpace->GlobalVirtualAddress->Vm;
  354. LOCK_WORKING_SET (Ws);
  355. NextEntry = MmSessionSpace->ImageList.Flink;
  356. while (NextEntry != &MmSessionSpace->ImageList) {
  357. Image = CONTAINING_RECORD(NextEntry, IMAGE_ENTRY_IN_SESSION, Link);
  358. if (Image->Address == BaseAddr) {
  359. RemoveEntryList (NextEntry);
  360. UNLOCK_WORKING_SET (Ws);
  361. ASSERT (MmSessionSpace->ImageLoadingCount >= 0);
  362. if (Image->ImageLoading == TRUE) {
  363. ASSERT (MmSessionSpace->ImageLoadingCount > 0);
  364. InterlockedDecrement (&MmSessionSpace->ImageLoadingCount);
  365. }
  366. ExFreePool (Image);
  367. return STATUS_SUCCESS;
  368. }
  369. NextEntry = NextEntry->Flink;
  370. }
  371. UNLOCK_WORKING_SET (Ws);
  372. return STATUS_NOT_FOUND;
  373. }
  374. PIMAGE_ENTRY_IN_SESSION
  375. MiSessionLookupImage (
  376. IN PVOID BaseAddress
  377. )
  378. /*++
  379. Routine Description:
  380. This routine looks up the image entry within the current session by the
  381. specified base address.
  382. Arguments:
  383. BaseAddress - Supplies the base address for the executable image.
  384. Return Value:
  385. The image entry within this session on success or NULL on failure.
  386. Environment:
  387. Kernel mode, APC_LEVEL and below, MmSystemLoadLock held.
  388. Note both the system load resource and the session working set
  389. mutex must be held to modify the list of images in this session.
  390. Either may be held to safely walk the list.
  391. --*/
  392. {
  393. PLIST_ENTRY NextEntry;
  394. PIMAGE_ENTRY_IN_SESSION Image;
  395. SYSLOAD_LOCK_OWNED_BY_ME ();
  396. NextEntry = MmSessionSpace->ImageList.Flink;
  397. while (NextEntry != &MmSessionSpace->ImageList) {
  398. Image = CONTAINING_RECORD(NextEntry, IMAGE_ENTRY_IN_SESSION, Link);
  399. if (Image->Address == BaseAddress) {
  400. return Image;
  401. }
  402. NextEntry = NextEntry->Flink;
  403. }
  404. return NULL;
  405. }
  406. VOID
  407. MiSessionUnloadAllImages (
  408. VOID
  409. )
  410. /*++
  411. Routine Description:
  412. This routine dereferences each image that has been loaded in the
  413. current session space.
  414. As each image is dereferenced, checks are made:
  415. If this session's reference count to the image reaches zero, the VA
  416. range in this session is deleted. If the reference count to the image
  417. in the SESSIONWIDE list drops to zero, then the SESSIONWIDE's VA
  418. reservation is removed and the address space is made available to any
  419. new image.
  420. If this is the last systemwide reference to the driver then the driver
  421. is deleted from memory.
  422. Arguments:
  423. None.
  424. Return Value:
  425. None.
  426. Environment:
  427. Kernel mode. This is called in one of two contexts:
  428. 1. the last thread in the last process of the current session space.
  429. 2. or by any thread in the SMSS process.
  430. Note both the system load resource and the session working set
  431. mutex must be held to modify the list of images in this session.
  432. Either may be held to safely walk the list.
  433. --*/
  434. {
  435. NTSTATUS Status;
  436. PLIST_ENTRY NextEntry;
  437. PIMAGE_ENTRY_IN_SESSION Module;
  438. PKLDR_DATA_TABLE_ENTRY ImageHandle;
  439. ASSERT (MmSessionSpace->ReferenceCount == 0);
  440. //
  441. // The session's working set lock does not need to be acquired here since
  442. // no thread can be faulting on these addresses.
  443. //
  444. NextEntry = MmSessionSpace->ImageList.Flink;
  445. while (NextEntry != &MmSessionSpace->ImageList) {
  446. Module = CONTAINING_RECORD(NextEntry, IMAGE_ENTRY_IN_SESSION, Link);
  447. //
  448. // Lookup the image entry in the system PsLoadedModuleList,
  449. // unload the image and delete it.
  450. //
  451. ImageHandle = MiLookupDataTableEntry (Module->Address, FALSE);
  452. ASSERT (ImageHandle);
  453. Status = MmUnloadSystemImage (ImageHandle);
  454. //
  455. // Restart the search at the beginning since the entry has been deleted.
  456. //
  457. ASSERT (MmSessionSpace->ReferenceCount == 0);
  458. NextEntry = MmSessionSpace->ImageList.Flink;
  459. }
  460. }
  461. VOID
  462. MiSessionWideInitializeAddresses (
  463. VOID
  464. )
  465. /*++
  466. Routine Description:
  467. This routine is called at system initialization to set up the group
  468. address list.
  469. Arguments:
  470. None.
  471. Return Value:
  472. None.
  473. Environment:
  474. Kernel mode.
  475. --*/
  476. {
  477. PVOID Bitmap;
  478. SIZE_T NumberOfPages;
  479. NumberOfPages = (MiSessionImageEnd - MiSessionImageStart) >> PAGE_SHIFT;
  480. Bitmap = ExAllocatePoolWithTag (PagedPool,
  481. ((NumberOfPages + 31) / 32) * 4,
  482. ' mM');
  483. if (Bitmap == NULL) {
  484. KeBugCheckEx (INSTALL_MORE_MEMORY,
  485. MmNumberOfPhysicalPages,
  486. MmLowestPhysicalPage,
  487. MmHighestPhysicalPage,
  488. 0x301);
  489. }
  490. RtlInitializeBitMap (&MiSessionWideVaBitMap,
  491. Bitmap,
  492. (ULONG) NumberOfPages);
  493. RtlClearAllBits (&MiSessionWideVaBitMap);
  494. return;
  495. }
  496. NTSTATUS
  497. MiSessionWideReserveImageAddress (
  498. IN PSECTION Section,
  499. OUT PVOID *AssignedAddress,
  500. OUT PSECTION *NewSectionPointer
  501. )
  502. /*++
  503. Routine Description:
  504. This routine allocates a range of virtual address space within
  505. session space. This address range is unique system-wide and in this
  506. manner, code and pristine data of session drivers can be shared across
  507. multiple sessions.
  508. This routine does not actually commit pages, but reserves the virtual
  509. address region for the named image. An entry is created here and attached
  510. to the current session space to track the loaded image. Thus if all
  511. the references to a given range go away, the range can then be reused.
  512. Arguments:
  513. Section - Supplies the section (and thus, the preferred address that the
  514. driver has been linked (rebased) at. If this address is
  515. available, the driver will require no relocation. The section
  516. is also used to derive the number of bytes to reserve.
  517. AssignedAddress - Supplies a pointer to a variable that receives the
  518. allocated address if the routine succeeds.
  519. Return Value:
  520. Returns STATUS_SUCCESS on success, various NTSTATUS codes on failure.
  521. Environment:
  522. Kernel mode, APC_LEVEL and below, MmSystemLoadLock held.
  523. --*/
  524. {
  525. ULONG StartPosition;
  526. ULONG NumberOfPtes;
  527. NTSTATUS Status;
  528. PWCHAR pName;
  529. PVOID NewAddress;
  530. ULONG_PTR SessionSpaceEnd;
  531. PVOID PreferredAddress;
  532. PCONTROL_AREA ControlArea;
  533. PIMAGE_ENTRY_IN_SESSION Image;
  534. PSESSION_GLOBAL_SUBSECTION_INFO GlobalSubs;
  535. PAGED_CODE();
  536. SYSLOAD_LOCK_OWNED_BY_ME ();
  537. ASSERT (PsGetCurrentProcess()->Flags & PS_PROCESS_FLAGS_IN_SESSION);
  538. ASSERT (MmIsAddressValid (MmSessionSpace) == TRUE);
  539. ASSERT (Section->u.Flags.Image == 1);
  540. *NewSectionPointer = NULL;
  541. ControlArea = Section->Segment->ControlArea;
  542. if (ControlArea->u.Flags.ImageMappedInSystemSpace == 1) {
  543. //
  544. // We are going to add a new entry to the loaded module list. We
  545. // have a section in hand. The case which must be handled carefully is:
  546. //
  547. // When a session image unloads, its entry is removed from the
  548. // loaded module list, but the section object itself may continue
  549. // to live on due to other references on the object. For session
  550. // images, the relocations and import snaps, verifier updates, etc,
  551. // are done directly to the prototype PTEs (the modified ones become
  552. // backed by the pagefile) at whatever address the image is loaded at.
  553. //
  554. // Thus, if the image's load address the second time around is different
  555. // from the first time around, this presents problems. Likewise, any
  556. // image it imports from is an issue, since if their load addresses
  557. // change, the IAT snaps need updating. This only gets more complicated
  558. // with recursive imports, imports where only some of the images have
  559. // lingering object reference counts, etc.
  560. //
  561. // There is a lingering object reference to this image since it
  562. // was last unloaded. It's ok just to fail this since it
  563. // can't be a user-generated reference and any kernel/driver
  564. // SEC_IMAGE reference would be extremely unusual (and short lived).
  565. //
  566. MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_IMAGE_ZOMBIE);
  567. KeDelayExecutionThread (KernelMode, FALSE, (PLARGE_INTEGER)&MmShortTime);
  568. return STATUS_CONFLICTING_ADDRESSES;
  569. }
  570. pName = NULL;
  571. StartPosition = NO_BITS_FOUND;
  572. PreferredAddress = Section->Segment->BasedAddress;
  573. NumberOfPtes = Section->Segment->TotalNumberOfPtes;
  574. SessionSpaceEnd = MiSessionImageEnd;
  575. //
  576. // Try to put the module into its requested address so it can be shared.
  577. //
  578. // If the requested address is not properly aligned or not in the session
  579. // space region, pick an address for it. This image will not be shared.
  580. //
  581. if ((BYTE_OFFSET (PreferredAddress) == 0) &&
  582. (PreferredAddress >= (PVOID) MiSessionImageStart) &&
  583. (PreferredAddress < (PVOID) MiSessionImageEnd)) {
  584. StartPosition = (ULONG) ((ULONG_PTR) PreferredAddress - MiSessionImageStart) >> PAGE_SHIFT;
  585. if (RtlAreBitsClear (&MiSessionWideVaBitMap,
  586. StartPosition,
  587. NumberOfPtes) == TRUE) {
  588. RtlSetBits (&MiSessionWideVaBitMap,
  589. StartPosition,
  590. NumberOfPtes);
  591. }
  592. else {
  593. PreferredAddress = NULL;
  594. }
  595. }
  596. else {
  597. PreferredAddress = NULL;
  598. }
  599. if (PreferredAddress == NULL) {
  600. StartPosition = RtlFindClearBitsAndSet (&MiSessionWideVaBitMap,
  601. NumberOfPtes,
  602. 0);
  603. if (StartPosition == NO_BITS_FOUND) {
  604. MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_NO_IMAGE_VA_SPACE);
  605. return STATUS_NO_MEMORY;
  606. }
  607. }
  608. NewAddress = (PVOID) (MiSessionImageStart + (StartPosition << PAGE_SHIFT));
  609. //
  610. // Create an entry for this image in the current session space.
  611. //
  612. Status = MiSessionInsertImage (NewAddress);
  613. if (!NT_SUCCESS (Status)) {
  614. Failure1:
  615. ASSERT (RtlAreBitsSet (&MiSessionWideVaBitMap,
  616. StartPosition,
  617. NumberOfPtes) == TRUE);
  618. RtlClearBits (&MiSessionWideVaBitMap,
  619. StartPosition,
  620. NumberOfPtes);
  621. return Status;
  622. }
  623. *AssignedAddress = NewAddress;
  624. GlobalSubs = NULL;
  625. //
  626. // This is the first load of this image in any session, so mark this
  627. // image so that copy-on-write flows through into read-write until
  628. // relocations (if any) and import image resolution is finished updating
  629. // all parts of this image. This way all future instantiations of this
  630. // image won't need to reprocess them (and can share the pages).
  631. // This is deliberately done this way so that any concurrent usermode
  632. // access to this image does not receive direct read-write privilege.
  633. //
  634. // Note images with less than native page subsection alignment are
  635. // currently marked copy on write for the entire image. Native page
  636. // aligned images have individual subsections with associated
  637. // permissions. Both image types get temporarily mapped read-write in
  638. // their first session mapping.
  639. //
  640. // After everything (relocations & image imports) is done, then
  641. // the real permissions (based on the PE header) can be applied and
  642. // the real PTEs automatically inherit the proper permissions
  643. // from the prototype PTEs.
  644. //
  645. // Since the fixups are only done once, they can then be
  646. // shared by any subsequent driver instantiations. Note that
  647. // any fixed-up pages are never written to the image, but are
  648. // instead converted to pagefile backing by the modified writer.
  649. //
  650. if (MiMarkImageInSystem (ControlArea) == FALSE) {
  651. ULONG Count;
  652. SIZE_T ViewSize;
  653. PVOID SrcVa;
  654. PVOID DestVa;
  655. PVOID SourceVa;
  656. PVOID DestinationVa;
  657. MMPTE PteContents;
  658. PMMPTE ProtoPte;
  659. PMMPTE PointerPte;
  660. PMMPTE LastPte;
  661. PFN_NUMBER ResidentPages;
  662. HANDLE NewSectionHandle;
  663. LARGE_INTEGER MaximumSectionSize;
  664. OBJECT_ATTRIBUTES ObjectAttributes;
  665. PSUBSECTION Subsection;
  666. PSUBSECTION SubsectionBase;
  667. PMMPTE PrototypePteBase;
  668. if ((ControlArea->u.Flags.GlobalOnlyPerSession == 0) &&
  669. (ControlArea->u.Flags.Rom == 0)) {
  670. Subsection = (PSUBSECTION)(ControlArea + 1);
  671. }
  672. else {
  673. Subsection = (PSUBSECTION)((PLARGE_CONTROL_AREA)ControlArea + 1);
  674. }
  675. SubsectionBase = Subsection;
  676. PrototypePteBase = Subsection->SubsectionBase;
  677. //
  678. // Count the number of global subsections.
  679. //
  680. Count = 0;
  681. do {
  682. if (Subsection->u.SubsectionFlags.GlobalMemory == 1) {
  683. Count += 1;
  684. }
  685. Subsection = Subsection->NextSubsection;
  686. } while (Subsection != NULL);
  687. //
  688. // Allocate pool to store the global subsection information as this
  689. // multi subsection image is going to be converted to a single
  690. // subsection pagefile section.
  691. //
  692. if (Count != 0) {
  693. GlobalSubs = ExAllocatePoolWithTag (PagedPool,
  694. (Count + 1) * sizeof (SESSION_GLOBAL_SUBSECTION_INFO),
  695. 'sGmM');
  696. if (GlobalSubs == NULL) {
  697. MiSessionRemoveImage (NewAddress);
  698. goto Failure1;
  699. }
  700. GlobalSubs[Count].PteCount = 0; // NULL-terminate the list.
  701. Count -= 1;
  702. Subsection = SubsectionBase;
  703. do {
  704. if (Subsection->u.SubsectionFlags.GlobalMemory == 1) {
  705. GlobalSubs[Count].PteIndex = Subsection->SubsectionBase - PrototypePteBase;
  706. GlobalSubs[Count].PteCount = Subsection->PtesInSubsection;
  707. GlobalSubs[Count].Protection = Subsection->u.SubsectionFlags.Protection;
  708. if (Count == 0) {
  709. break;
  710. }
  711. Count -= 1;
  712. }
  713. Subsection = Subsection->NextSubsection;
  714. } while (Subsection != NULL);
  715. ASSERT (Count == 0);
  716. }
  717. MaximumSectionSize.QuadPart = NumberOfPtes << PAGE_SHIFT;
  718. ViewSize = 0;
  719. InitializeObjectAttributes (&ObjectAttributes,
  720. NULL,
  721. (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
  722. NULL,
  723. NULL);
  724. //
  725. // Create a pagefile-backed section to copy the image into.
  726. //
  727. Status = ZwCreateSection (&NewSectionHandle,
  728. SECTION_ALL_ACCESS,
  729. &ObjectAttributes,
  730. &MaximumSectionSize,
  731. PAGE_EXECUTE_READWRITE,
  732. SEC_COMMIT,
  733. NULL);
  734. if (!NT_SUCCESS (Status)) {
  735. if (GlobalSubs != NULL) {
  736. ExFreePool (GlobalSubs);
  737. }
  738. MiSessionRemoveImage (NewAddress);
  739. goto Failure1;
  740. }
  741. //
  742. // Now reference the section handle. If this fails something is
  743. // very wrong because it is a kernel handle.
  744. //
  745. // N.B. ObRef sets SectionPointer to NULL on failure.
  746. //
  747. Status = ObReferenceObjectByHandle (NewSectionHandle,
  748. SECTION_MAP_EXECUTE,
  749. MmSectionObjectType,
  750. KernelMode,
  751. (PVOID *) NewSectionPointer,
  752. (POBJECT_HANDLE_INFORMATION) NULL);
  753. ZwClose (NewSectionHandle);
  754. if (!NT_SUCCESS (Status)) {
  755. if (GlobalSubs != NULL) {
  756. ExFreePool (GlobalSubs);
  757. }
  758. MiSessionRemoveImage (NewAddress);
  759. goto Failure1;
  760. }
  761. //
  762. // Map the destination. Deliberately put the destination in system
  763. // space and the source in session space to increase the chances
  764. // that enough virtual address space can be found.
  765. //
  766. Status = MmMapViewInSystemSpace (*NewSectionPointer,
  767. &DestinationVa,
  768. &ViewSize);
  769. if (!NT_SUCCESS (Status)) {
  770. if (GlobalSubs != NULL) {
  771. ExFreePool (GlobalSubs);
  772. }
  773. ObDereferenceObject (*NewSectionPointer);
  774. MiSessionRemoveImage (NewAddress);
  775. goto Failure1;
  776. }
  777. //
  778. // Map the source.
  779. //
  780. Status = MmMapViewInSessionSpace (Section, &SourceVa, &ViewSize);
  781. if (!NT_SUCCESS (Status)) {
  782. if (GlobalSubs != NULL) {
  783. ExFreePool (GlobalSubs);
  784. }
  785. MmUnmapViewInSystemSpace (DestinationVa);
  786. ObDereferenceObject (*NewSectionPointer);
  787. MiSessionRemoveImage (NewAddress);
  788. goto Failure1;
  789. }
  790. //
  791. // Copy the pristine executable.
  792. //
  793. ProtoPte = Section->Segment->PrototypePte;
  794. LastPte = ProtoPte + NumberOfPtes;
  795. SrcVa = SourceVa;
  796. DestVa = DestinationVa;
  797. while (ProtoPte < LastPte) {
  798. PteContents = *ProtoPte;
  799. if ((PteContents.u.Hard.Valid == 1) ||
  800. (PteContents.u.Soft.Protection != MM_NOACCESS)) {
  801. RtlCopyMemory (DestVa, SrcVa, PAGE_SIZE);
  802. }
  803. else {
  804. //
  805. // The source PTE is no access, just leave the destination
  806. // PTE as demand zero.
  807. //
  808. }
  809. ProtoPte += 1;
  810. SrcVa = ((PCHAR)SrcVa + PAGE_SIZE);
  811. DestVa = ((PCHAR)DestVa + PAGE_SIZE);
  812. }
  813. Status = MmUnmapViewInSystemSpace (DestinationVa);
  814. if (!NT_SUCCESS (Status)) {
  815. ASSERT (FALSE);
  816. }
  817. //
  818. // Delete the source image pages as the BSS ones have been expanded
  819. // into private demand zero as part of the copy above. If we don't
  820. // delete them all, the private demand zero would go on the modified
  821. // list with a PTE address pointing at the session view space which
  822. // will have been reused.
  823. //
  824. PointerPte = MiGetPteAddress (SourceVa);
  825. MiDeleteSystemPagableVm (PointerPte,
  826. NumberOfPtes,
  827. ZeroKernelPte,
  828. TRUE,
  829. &ResidentPages);
  830. MI_INCREMENT_RESIDENT_AVAILABLE (ResidentPages,
  831. MM_RESAVAIL_FREE_UNLOAD_SYSTEM_IMAGE);
  832. Status = MmUnmapViewInSessionSpace (SourceVa);
  833. if (!NT_SUCCESS (Status)) {
  834. ASSERT (FALSE);
  835. }
  836. //
  837. // Our caller will be using the new pagefile-backed section we
  838. // just created. Copy over useful fields now and then dereference
  839. // the entry section.
  840. //
  841. ((PSECTION)*NewSectionPointer)->Segment->u1.ImageCommitment =
  842. Section->Segment->u1.ImageCommitment;
  843. ((PSECTION)*NewSectionPointer)->Segment->BasedAddress =
  844. Section->Segment->BasedAddress;
  845. ObDereferenceObject (Section);
  846. }
  847. else {
  848. *NewSectionPointer = NULL;
  849. }
  850. Image = MiSessionLookupImage (NewAddress);
  851. if (Image != NULL) {
  852. ASSERT (Image->GlobalSubs == NULL);
  853. Image->GlobalSubs = GlobalSubs;
  854. ASSERT (Image->ImageLoading == FALSE);
  855. Image->ImageLoading = TRUE;
  856. ASSERT (MmSessionSpace->ImageLoadingCount >= 0);
  857. InterlockedIncrement (&MmSessionSpace->ImageLoadingCount);
  858. }
  859. else {
  860. ASSERT (FALSE);
  861. }
  862. return STATUS_SUCCESS;
  863. }
  864. VOID
  865. MiRemoveImageSessionWide (
  866. IN PKLDR_DATA_TABLE_ENTRY DataTableEntry OPTIONAL,
  867. IN PVOID BaseAddress,
  868. IN ULONG_PTR NumberOfBytes
  869. )
  870. /*++
  871. Routine Description:
  872. Delete the image space region from the current session space.
  873. This dereferences the globally allocated SessionWide region.
  874. The SessionWide region will be deleted if the reference count goes to zero.
  875. Arguments:
  876. DataTableEntry - Supplies the (optional) loader entry.
  877. BaseAddress - Supplies the address the driver is loaded at.
  878. NumberOfBytes - Supplies the number of bytes used by the driver.
  879. Return Value:
  880. Returns STATUS_SUCCESS on success, STATUS_NOT_FOUND on failure.
  881. Environment:
  882. Kernel mode, APC_LEVEL and below, MmSystemLoadLock held.
  883. --*/
  884. {
  885. ULONG StartPosition;
  886. PAGED_CODE();
  887. SYSLOAD_LOCK_OWNED_BY_ME ();
  888. ASSERT (MmIsAddressValid (MmSessionSpace) == TRUE);
  889. //
  890. // There is no data table entry if we are encountering an error during
  891. // the driver's first load (one hasn't been created yet). But we still
  892. // need to clear out the in-use bits.
  893. //
  894. if ((DataTableEntry == NULL) || (DataTableEntry->LoadCount == 1)) {
  895. StartPosition = (ULONG)(((ULONG_PTR) BaseAddress - MiSessionImageStart) >> PAGE_SHIFT);
  896. ASSERT (RtlAreBitsSet (&MiSessionWideVaBitMap,
  897. StartPosition,
  898. (ULONG) (NumberOfBytes >> PAGE_SHIFT)) == TRUE);
  899. RtlClearBits (&MiSessionWideVaBitMap,
  900. StartPosition,
  901. (ULONG) (NumberOfBytes >> PAGE_SHIFT));
  902. }
  903. //
  904. // Remove the image reference from the current session space.
  905. //
  906. MiSessionRemoveImage (BaseAddress);
  907. return;
  908. }