Source code of Windows XP (NT5)
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.

4321 lines
107 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. session.c
  5. Abstract:
  6. This module contains the routines which implement the creation and
  7. deletion of session spaces along with associated support routines.
  8. Author:
  9. Landy Wang (landyw) 05-Dec-1997
  10. Revision History:
  11. --*/
  12. #include "mi.h"
  13. ULONG MiSessionCount;
  14. LONG MmSessionDataPages;
  15. #define MM_MAXIMUM_CONCURRENT_SESSIONS 16384
  16. FAST_MUTEX MiSessionIdMutex;
  17. PRTL_BITMAP MiSessionIdBitmap;
  18. #if (_MI_PAGING_LEVELS >= 3)
  19. #if defined(_AMD64_)
  20. #define MI_SESSION_COMMIT_CHARGE 5
  21. #elif defined(_IA64_)
  22. #define MI_SESSION_COMMIT_CHARGE 5
  23. extern REGION_MAP_INFO MmSessionMapInfo;
  24. extern PFN_NUMBER MmSessionParentTablePage;
  25. #else
  26. #define MI_SESSION_COMMIT_CHARGE 4
  27. #endif
  28. #else
  29. #define MI_SESSION_COMMIT_CHARGE 3
  30. #endif
  31. VOID
  32. MiSessionAddProcess (
  33. PEPROCESS NewProcess
  34. );
  35. VOID
  36. MiSessionRemoveProcess (
  37. VOID
  38. );
  39. VOID
  40. MiInitializeSessionIds (
  41. VOID
  42. );
  43. NTSTATUS
  44. MiSessionCreateInternal (
  45. OUT PULONG SessionId
  46. );
  47. NTSTATUS
  48. MiSessionCommitPageTables (
  49. IN PVOID StartVa,
  50. IN PVOID EndVa
  51. );
  52. VOID
  53. MiDereferenceSession (
  54. VOID
  55. );
  56. VOID
  57. MiSessionDeletePde (
  58. IN PMMPTE Pde,
  59. IN LOGICAL WorkingSetInitialized,
  60. IN PMMPTE SelfMapPde
  61. );
  62. VOID
  63. MiDereferenceSessionFinal (
  64. VOID
  65. );
  66. #if DBG
  67. VOID
  68. MiCheckSessionVirtualSpace (
  69. IN PVOID VirtualAddress,
  70. IN SIZE_T NumberOfBytes
  71. );
  72. #endif
  73. #ifdef ALLOC_PRAGMA
  74. #pragma alloc_text(INIT,MiInitializeSessionIds)
  75. #pragma alloc_text(PAGE, MmSessionSetUnloadAddress)
  76. #pragma alloc_text(PAGE, MmSessionCreate)
  77. #pragma alloc_text(PAGE, MmSessionDelete)
  78. #pragma alloc_text(PAGE, MmGetSessionLocaleId)
  79. #pragma alloc_text(PAGE, MmSetSessionLocaleId)
  80. #pragma alloc_text(PAGE, MmQuitNextSession)
  81. #pragma alloc_text(PAGE, MiDereferenceSession)
  82. #pragma alloc_text(PAGE, MiDetachFromSecureProcessInSession)
  83. #if DBG
  84. #pragma alloc_text(PAGE, MiCheckSessionVirtualSpace)
  85. #endif
  86. #pragma alloc_text(PAGELK, MiSessionCreateInternal)
  87. #pragma alloc_text(PAGELK, MiDereferenceSessionFinal)
  88. #endif
  89. VOID
  90. MiSessionLeader (
  91. IN PEPROCESS Process
  92. )
  93. /*++
  94. Routine Description:
  95. Mark the argument process as having the ability to create or delete session
  96. spaces. This is only granted to the session manager process.
  97. Arguments:
  98. Process - Supplies a pointer to the privileged process.
  99. Return Value:
  100. None.
  101. Environment:
  102. Kernel mode.
  103. --*/
  104. {
  105. KIRQL OldIrql;
  106. LOCK_EXPANSION (OldIrql);
  107. Process->Vm.Flags.SessionLeader = 1;
  108. UNLOCK_EXPANSION (OldIrql);
  109. }
  110. VOID
  111. MmSessionSetUnloadAddress (
  112. IN PDRIVER_OBJECT pWin32KDevice
  113. )
  114. /*++
  115. Routine Description:
  116. Copy the win32k.sys driver object to the session structure for use during
  117. unload.
  118. Arguments:
  119. NewProcess - Supplies a pointer to the win32k driver object.
  120. Return Value:
  121. None.
  122. Environment:
  123. Kernel mode.
  124. --*/
  125. {
  126. PDRIVER_OBJECT Destination;
  127. ASSERT (PsGetCurrentProcess()->Flags & PS_PROCESS_FLAGS_IN_SESSION);
  128. ASSERT (MmIsAddressValid(MmSessionSpace) == TRUE);
  129. Destination = &MmSessionSpace->Win32KDriverObject;
  130. RtlCopyMemory (Destination, pWin32KDevice, sizeof(DRIVER_OBJECT));
  131. }
  132. VOID
  133. MiSessionAddProcess (
  134. PEPROCESS NewProcess
  135. )
  136. /*++
  137. Routine Description:
  138. Add the new process to the current session space.
  139. Arguments:
  140. NewProcess - Supplies a pointer to the process being created.
  141. Return Value:
  142. None.
  143. Environment:
  144. Kernel mode, APCs disabled.
  145. --*/
  146. {
  147. KIRQL OldIrql;
  148. PMM_SESSION_SPACE SessionGlobal;
  149. //
  150. // If the calling process has no session, then the new process won't get
  151. // one either.
  152. //
  153. if ((PsGetCurrentProcess()->Flags & PS_PROCESS_FLAGS_IN_SESSION)== 0) {
  154. return;
  155. }
  156. ASSERT (MmIsAddressValid (MmSessionSpace) == TRUE);
  157. SessionGlobal = SESSION_GLOBAL(MmSessionSpace);
  158. InterlockedIncrement ((PLONG)&SessionGlobal->ReferenceCount);
  159. InterlockedIncrement (&SessionGlobal->ProcessReferenceToSession);
  160. //
  161. // Once the Session pointer in the EPROCESS is set it can never
  162. // be cleared because it is accessed lock-free.
  163. //
  164. ASSERT (NewProcess->Session == NULL);
  165. NewProcess->Session = (PVOID) SessionGlobal;
  166. #if defined(_IA64_)
  167. KeAddSessionSpace(&NewProcess->Pcb,
  168. &SessionGlobal->SessionMapInfo,
  169. SessionGlobal->PageDirectoryParentPage);
  170. #endif
  171. //
  172. // Link the process entry into the session space and WSL structures.
  173. //
  174. LOCK_EXPANSION (OldIrql);
  175. if (IsListEmpty(&SessionGlobal->ProcessList)) {
  176. if (MmSessionSpace->Vm.Flags.AllowWorkingSetAdjustment == FALSE) {
  177. ASSERT (MmSessionSpace->u.Flags.WorkingSetInserted == 0);
  178. MmSessionSpace->Vm.Flags.AllowWorkingSetAdjustment = TRUE;
  179. InsertTailList (&MmWorkingSetExpansionHead.ListHead,
  180. &SessionGlobal->Vm.WorkingSetExpansionLinks);
  181. MmSessionSpace->u.Flags.WorkingSetInserted = 1;
  182. }
  183. }
  184. InsertTailList (&SessionGlobal->ProcessList, &NewProcess->SessionProcessLinks);
  185. UNLOCK_EXPANSION (OldIrql);
  186. PS_SET_BITS (&NewProcess->Flags, PS_PROCESS_FLAGS_IN_SESSION);
  187. }
  188. VOID
  189. MiSessionRemoveProcess (
  190. VOID
  191. )
  192. /*++
  193. Routine Description:
  194. This routine removes the current process from the current session space.
  195. This may trigger a substantial round of dereferencing and resource freeing
  196. if it is also the last process in the session, (holding the last image
  197. in the group, etc).
  198. Arguments:
  199. None.
  200. Return Value:
  201. None.
  202. Environment:
  203. Kernel mode, APC_LEVEL and below, but queueing of APCs to this thread has
  204. been permanently disabled. This is the last thread in the process
  205. being deleted. The caller has ensured that this process is not
  206. on the expansion list and therefore there can be no races in regards to
  207. trimming.
  208. --*/
  209. {
  210. KIRQL OldIrql;
  211. PEPROCESS CurrentProcess;
  212. #if DBG
  213. ULONG Found;
  214. PEPROCESS Process;
  215. PLIST_ENTRY NextEntry;
  216. PMM_SESSION_SPACE SessionGlobal;
  217. #endif
  218. CurrentProcess = PsGetCurrentProcess();
  219. if (((CurrentProcess->Flags & PS_PROCESS_FLAGS_IN_SESSION) == 0) ||
  220. (CurrentProcess->Vm.Flags.SessionLeader == 1)) {
  221. return;
  222. }
  223. ASSERT (MmIsAddressValid (MmSessionSpace) == TRUE);
  224. //
  225. // Remove this process from the list of processes in the current session.
  226. //
  227. LOCK_EXPANSION (OldIrql);
  228. #if DBG
  229. SessionGlobal = SESSION_GLOBAL(MmSessionSpace);
  230. Found = 0;
  231. NextEntry = SessionGlobal->ProcessList.Flink;
  232. while (NextEntry != &SessionGlobal->ProcessList) {
  233. Process = CONTAINING_RECORD (NextEntry, EPROCESS, SessionProcessLinks);
  234. if (Process == CurrentProcess) {
  235. Found = 1;
  236. }
  237. NextEntry = NextEntry->Flink;
  238. }
  239. ASSERT (Found == 1);
  240. #endif
  241. RemoveEntryList (&CurrentProcess->SessionProcessLinks);
  242. UNLOCK_EXPANSION (OldIrql);
  243. //
  244. // Decrement this process' reference count to the session. If this
  245. // is the last reference, then the entire session will be destroyed
  246. // upon return. This includes unloading drivers, unmapping pools,
  247. // freeing page tables, etc.
  248. //
  249. MiDereferenceSession ();
  250. }
  251. LCID
  252. MmGetSessionLocaleId (
  253. VOID
  254. )
  255. /*++
  256. Routine Description:
  257. This routine gets the locale ID for the current session.
  258. Arguments:
  259. None.
  260. Return Value:
  261. The locale ID for the current session.
  262. Environment:
  263. PASSIVE_LEVEL, the caller must supply any desired synchronization.
  264. --*/
  265. {
  266. PEPROCESS Process;
  267. PMM_SESSION_SPACE SessionGlobal;
  268. PAGED_CODE ();
  269. Process = PsGetCurrentProcess ();
  270. if (Process->Vm.Flags.SessionLeader == 1) {
  271. //
  272. // smss may transiently have a session space but that's of no interest
  273. // to our caller.
  274. //
  275. return PsDefaultThreadLocaleId;
  276. }
  277. //
  278. // The Session field of the EPROCESS is never cleared once set so these
  279. // checks can be done lock free.
  280. //
  281. SessionGlobal = (PMM_SESSION_SPACE) Process->Session;
  282. if (SessionGlobal == NULL) {
  283. //
  284. // The system process has no session space.
  285. //
  286. return PsDefaultThreadLocaleId;
  287. }
  288. SessionGlobal = (PMM_SESSION_SPACE) Process->Session;
  289. return SessionGlobal->LocaleId;
  290. }
  291. VOID
  292. MmSetSessionLocaleId (
  293. IN LCID LocaleId
  294. )
  295. /*++
  296. Routine Description:
  297. This routine sets the locale ID for the current session.
  298. Arguments:
  299. LocaleId - Supplies the desired locale ID.
  300. Return Value:
  301. None.
  302. Environment:
  303. PASSIVE_LEVEL, the caller must supply any desired synchronization.
  304. --*/
  305. {
  306. PEPROCESS Process;
  307. PMM_SESSION_SPACE SessionGlobal;
  308. PAGED_CODE ();
  309. Process = PsGetCurrentProcess ();
  310. if (Process->Vm.Flags.SessionLeader == 1) {
  311. //
  312. // smss may transiently have a session space but that's of no interest
  313. // to our caller.
  314. //
  315. PsDefaultThreadLocaleId = LocaleId;
  316. return;
  317. }
  318. //
  319. // The Session field of the EPROCESS is never cleared once set so these
  320. // checks can be done lock free.
  321. //
  322. SessionGlobal = (PMM_SESSION_SPACE) Process->Session;
  323. if (SessionGlobal == NULL) {
  324. //
  325. // The system process has no session space.
  326. //
  327. PsDefaultThreadLocaleId = LocaleId;
  328. return;
  329. }
  330. SessionGlobal = (PMM_SESSION_SPACE) Process->Session;
  331. SessionGlobal->LocaleId = LocaleId;
  332. }
  333. VOID
  334. MiInitializeSessionIds (
  335. VOID
  336. )
  337. /*++
  338. Routine Description:
  339. This routine creates and initializes session ID allocation/deallocation.
  340. Arguments:
  341. None.
  342. Return Value:
  343. None.
  344. --*/
  345. {
  346. //
  347. // If this ever grows beyond 2x the page size, both the allocation and
  348. // deletion code will need to be updated.
  349. //
  350. ASSERT (sizeof(MM_SESSION_SPACE) <= 2 * PAGE_SIZE);
  351. ExInitializeFastMutex (&MiSessionIdMutex);
  352. MiCreateBitMap (&MiSessionIdBitmap,
  353. MM_MAXIMUM_CONCURRENT_SESSIONS,
  354. PagedPool);
  355. if (MiSessionIdBitmap == NULL) {
  356. MiCreateBitMap (&MiSessionIdBitmap,
  357. MM_MAXIMUM_CONCURRENT_SESSIONS,
  358. NonPagedPoolMustSucceed);
  359. }
  360. RtlClearAllBits (MiSessionIdBitmap);
  361. }
  362. NTSTATUS
  363. MiSessionCreateInternal (
  364. OUT PULONG SessionId
  365. )
  366. /*++
  367. Routine Description:
  368. This routine creates the data structure that describes and maintains
  369. the session space. It resides at the beginning of the session space.
  370. Carefully construct the first page mapping to bootstrap the fault
  371. handler which relies on the session space data structure being
  372. present and valid.
  373. In NT32, this initial mapping for the portion of session space
  374. mapped by the first PDE will automatically be inherited by all child
  375. processes when the system copies the system portion of the page
  376. directory for new address spaces. Additional entries are faulted
  377. in by the session space fault handler, which references this structure.
  378. For NT64, everything is automatically inherited.
  379. This routine commits virtual memory within the current session space with
  380. backing pages. The virtual addresses within session space are
  381. allocated with a separate facility in the image management facility.
  382. This is because images must be at a unique system wide virtual address.
  383. Arguments:
  384. SessionId - Supplies a pointer to place the new session ID into.
  385. Return Value:
  386. STATUS_SUCCESS if all went well, various failure status codes
  387. if the session was not created.
  388. Environment:
  389. Kernel mode, no mutexes held.
  390. --*/
  391. {
  392. KIRQL OldIrql;
  393. PMMPTE PointerPde;
  394. PMMPTE PointerPte;
  395. PMMPTE GlobalMappingPte;
  396. NTSTATUS Status;
  397. PMM_SESSION_SPACE SessionSpace;
  398. PMM_SESSION_SPACE SessionGlobal;
  399. PFN_NUMBER ResidentPages;
  400. LOGICAL GotCommit;
  401. LOGICAL GotPages;
  402. LOGICAL GotUnmappedDataPage;
  403. LOGICAL PoolInitialized;
  404. PMMPFN Pfn1;
  405. MMPTE TempPte;
  406. MMPTE TempPte2;
  407. ULONG_PTR Va;
  408. PFN_NUMBER DataPage;
  409. PFN_NUMBER DataPage2;
  410. PFN_NUMBER PageTablePage;
  411. ULONG PageColor;
  412. ULONG ProcessFlags;
  413. ULONG NewProcessFlags;
  414. PEPROCESS Process;
  415. #if (_MI_PAGING_LEVELS < 3)
  416. SIZE_T PageTableBytes;
  417. PMMPTE PageTables;
  418. #else
  419. PMMPTE PointerPpe;
  420. PFN_NUMBER PageDirectoryPage;
  421. PFN_NUMBER PageDirectoryParentPage;
  422. #endif
  423. #if (_MI_PAGING_LEVELS >= 4)
  424. PMMPTE PointerPxe;
  425. #endif
  426. GotCommit = FALSE;
  427. GotPages = FALSE;
  428. GotUnmappedDataPage = FALSE;
  429. GlobalMappingPte = NULL;
  430. PoolInitialized = FALSE;
  431. //
  432. // Initializing these are not needed for correctness
  433. // but without it the compiler cannot compile this code
  434. // W4 to check for use of uninitialized variables.
  435. //
  436. DataPage = 0;
  437. DataPage2 = 0;
  438. PageTablePage = 0;
  439. PointerPte = NULL;
  440. #if (_MI_PAGING_LEVELS >= 3)
  441. PageDirectoryPage = 0;
  442. PageDirectoryParentPage = 0;
  443. #endif
  444. Process = PsGetCurrentProcess();
  445. #if defined(_IA64_)
  446. ASSERT (MI_GET_PAGE_FRAME_FROM_PTE((PMMPTE)(&Process->Pcb.SessionParentBase)) == MmSessionParentTablePage);
  447. #else
  448. ASSERT (MmIsAddressValid(MmSessionSpace) == FALSE);
  449. #endif
  450. //
  451. // Check for concurrent session creation attempts.
  452. //
  453. ProcessFlags = Process->Flags;
  454. while (TRUE) {
  455. if (ProcessFlags & PS_PROCESS_FLAGS_CREATING_SESSION) {
  456. return STATUS_ALREADY_COMMITTED;
  457. }
  458. NewProcessFlags = (ProcessFlags | PS_PROCESS_FLAGS_CREATING_SESSION);
  459. NewProcessFlags = InterlockedCompareExchange ((PLONG)&Process->Flags,
  460. (LONG)NewProcessFlags,
  461. (LONG)ProcessFlags);
  462. if (NewProcessFlags == ProcessFlags) {
  463. break;
  464. }
  465. //
  466. // The structure changed beneath us. Use the return value from the
  467. // exchange and try it all again.
  468. //
  469. ProcessFlags = NewProcessFlags;
  470. }
  471. ASSERT (Process->Flags & PS_PROCESS_FLAGS_CREATING_SESSION);
  472. #if (_MI_PAGING_LEVELS < 3)
  473. PageTableBytes = MI_SESSION_SPACE_MAXIMUM_PAGE_TABLES * sizeof (MMPTE);
  474. PageTables = (PMMPTE) ExAllocatePoolWithTag (NonPagedPool,
  475. PageTableBytes,
  476. 'tHmM');
  477. if (PageTables == NULL) {
  478. ASSERT (Process->Flags & PS_PROCESS_FLAGS_CREATING_SESSION);
  479. PS_CLEAR_BITS (&Process->Flags, PS_PROCESS_FLAGS_CREATING_SESSION);
  480. MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_NO_NONPAGED_POOL);
  481. return STATUS_INSUFFICIENT_RESOURCES;
  482. }
  483. RtlZeroMemory (PageTables, PageTableBytes);
  484. #endif
  485. //
  486. // Select a free session ID.
  487. //
  488. ExAcquireFastMutex (&MiSessionIdMutex);
  489. *SessionId = RtlFindClearBitsAndSet (MiSessionIdBitmap, 1, 0);
  490. ExReleaseFastMutex (&MiSessionIdMutex);
  491. if (*SessionId == NO_BITS_FOUND) {
  492. ASSERT (Process->Flags & PS_PROCESS_FLAGS_CREATING_SESSION);
  493. PS_CLEAR_BITS (&Process->Flags, PS_PROCESS_FLAGS_CREATING_SESSION);
  494. #if (_MI_PAGING_LEVELS < 3)
  495. ExFreePool (PageTables);
  496. #endif
  497. MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_NO_IDS);
  498. return STATUS_NO_MEMORY;
  499. }
  500. //
  501. // Lock down this routine in preparation for the PFN lock acquisition.
  502. // Note this is done prior to the commitment charges just to simplify
  503. // error handling.
  504. //
  505. MmLockPagableSectionByHandle (ExPageLockHandle);
  506. //
  507. // Charge commitment.
  508. //
  509. ResidentPages = MI_SESSION_COMMIT_CHARGE;
  510. if (MiChargeCommitment (ResidentPages, NULL) == FALSE) {
  511. MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_NO_COMMIT);
  512. goto Failure;
  513. }
  514. GotCommit = TRUE;
  515. MM_TRACK_COMMIT (MM_DBG_COMMIT_SESSION_CREATE, ResidentPages);
  516. //
  517. // Reserve global system PTEs to map the data pages with.
  518. //
  519. GlobalMappingPte = MiReserveSystemPtes (2, SystemPteSpace);
  520. if (GlobalMappingPte == NULL) {
  521. MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_NO_SYSPTES);
  522. goto Failure;
  523. }
  524. //
  525. // Ensure the resident physical pages are available.
  526. //
  527. LOCK_PFN (OldIrql);
  528. if ((SPFN_NUMBER)(ResidentPages + MI_SESSION_SPACE_WORKING_SET_MINIMUM) > MI_NONPAGABLE_MEMORY_AVAILABLE()) {
  529. UNLOCK_PFN (OldIrql);
  530. MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_NO_RESIDENT);
  531. goto Failure;
  532. }
  533. GotPages = TRUE;
  534. MmResidentAvailablePages -= (ResidentPages + MI_SESSION_SPACE_WORKING_SET_MINIMUM);
  535. MM_BUMP_COUNTER(40, ResidentPages + MI_SESSION_SPACE_WORKING_SET_MINIMUM);
  536. //
  537. // Allocate both session space data pages first as on some architectures
  538. // a region ID will be used immediately for the TB references as the
  539. // PTE mappings are initialized.
  540. //
  541. TempPte.u.Long = ValidKernelPte.u.Long;
  542. MiEnsureAvailablePageOrWait (NULL, NULL);
  543. PageColor = MI_GET_PAGE_COLOR_FROM_VA (NULL);
  544. DataPage = MiRemoveZeroPageMayReleaseLocks (PageColor, OldIrql);
  545. TempPte.u.Hard.PageFrameNumber = DataPage;
  546. TempPte2.u.Long = ValidKernelPte.u.Long;
  547. MiEnsureAvailablePageOrWait (NULL, NULL);
  548. PageColor = MI_GET_PAGE_COLOR_FROM_VA (NULL);
  549. DataPage2 = MiRemoveZeroPageMayReleaseLocks (PageColor, OldIrql);
  550. TempPte2.u.Hard.PageFrameNumber = DataPage2;
  551. GotUnmappedDataPage = TRUE;
  552. //
  553. // Map the data pages immediately in global space. Some architectures
  554. // use a region ID which is used immediately for the TB references after
  555. // the PTE mappings are initialized.
  556. //
  557. //
  558. // The global bit can be left on for the global mappings (unlike the
  559. // session space mapping which must be have the global bit off since
  560. // we need to make sure the TB entry is flushed when we switch to
  561. // a process in a different session space).
  562. //
  563. MI_WRITE_VALID_PTE (GlobalMappingPte, TempPte);
  564. MI_WRITE_VALID_PTE (GlobalMappingPte + 1, TempPte2);
  565. SessionGlobal = (PMM_SESSION_SPACE) MiGetVirtualAddressMappedByPte (GlobalMappingPte);
  566. #if (_MI_PAGING_LEVELS >= 3)
  567. //
  568. // Initialize the page directory parent page.
  569. //
  570. MiEnsureAvailablePageOrWait (NULL, NULL);
  571. PageColor = MI_GET_PAGE_COLOR_FROM_VA (NULL);
  572. PageDirectoryParentPage = MiRemoveZeroPageMayReleaseLocks (PageColor, OldIrql);
  573. TempPte.u.Long = ValidKernelPdeLocal.u.Long;
  574. TempPte.u.Hard.PageFrameNumber = PageDirectoryParentPage;
  575. #if defined(_IA64_)
  576. ASSERT (MI_GET_PAGE_FRAME_FROM_PTE((PMMPTE)(&Process->Pcb.SessionParentBase)) == MmSessionParentTablePage);
  577. //
  578. // In order to prevent races with threads on other processors context
  579. // switching into this process, initialize the region registers and
  580. // translation registers stored in the KPROCESS now. Otherwise
  581. // access to the top level parent can go away which would be fatal.
  582. //
  583. // Note this could not be done until both the top level page and the
  584. // session data page were acquired.
  585. //
  586. // The top level session entry is mapped with a translation register.
  587. // Replacing a current TR entry requires a purge first if the virtual
  588. // address and RID are the same in the new and old entries.
  589. //
  590. KeEnableSessionSharing (&SessionGlobal->SessionMapInfo,
  591. PageDirectoryParentPage);
  592. //
  593. // Install the selfmap entry for this session space.
  594. //
  595. PointerPpe = KSEG_ADDRESS (PageDirectoryParentPage);
  596. PointerPpe[MiGetPpeOffset(PDE_STBASE)] = TempPte;
  597. PointerPpe = MiGetPpeAddress ((PVOID)MmSessionSpace);
  598. MiInitializePfnForOtherProcess (PageDirectoryParentPage, PointerPpe, 0);
  599. Pfn1 = MI_PFN_ELEMENT (PageDirectoryParentPage);
  600. Pfn1->u4.PteFrame = PageDirectoryParentPage;
  601. #else
  602. //
  603. // The global bit is masked off since we need to make sure the TB entry
  604. // is flushed when we switch to a process in a different session space.
  605. //
  606. TempPte.u.Long = ValidKernelPdeLocal.u.Long;
  607. TempPte.u.Hard.PageFrameNumber = PageDirectoryParentPage;
  608. PointerPxe = MiGetPxeAddress ((PVOID)MmSessionSpace);
  609. ASSERT (PointerPxe->u.Long == 0);
  610. *PointerPxe = TempPte;
  611. //
  612. // Do not reference the top level parent page as it belongs to the
  613. // current process (SMSS).
  614. //
  615. MiInitializePfnForOtherProcess (PageDirectoryParentPage, PointerPxe, 0);
  616. Pfn1 = MI_PFN_ELEMENT (PageDirectoryParentPage);
  617. Pfn1->u4.PteFrame = 0;
  618. KeFillEntryTb ((PHARDWARE_PTE) PointerPxe,
  619. MiGetVirtualAddressMappedByPte (PointerPxe),
  620. FALSE);
  621. #endif
  622. ASSERT (MI_PFN_ELEMENT(PageDirectoryParentPage)->u1.WsIndex == 0);
  623. //
  624. // Initialize the page directory page.
  625. //
  626. MiEnsureAvailablePageOrWait (NULL, NULL);
  627. PageColor = MI_GET_PAGE_COLOR_FROM_VA (NULL);
  628. PageDirectoryPage = MiRemoveZeroPageMayReleaseLocks (PageColor, OldIrql);
  629. //
  630. // The global bit is masked off since we need to make sure the TB entry
  631. // is flushed when we switch to a process in a different session space.
  632. //
  633. TempPte.u.Long = ValidKernelPdeLocal.u.Long;
  634. TempPte.u.Hard.PageFrameNumber = PageDirectoryPage;
  635. PointerPpe = MiGetPpeAddress ((PVOID)MmSessionSpace);
  636. ASSERT (PointerPpe->u.Long == 0);
  637. *PointerPpe = TempPte;
  638. #if defined(_IA64_)
  639. //
  640. // IA64 can reference the top level parent page here because a unique
  641. // one is allocated per process.
  642. //
  643. MiInitializePfnForOtherProcess (PageDirectoryPage, PointerPpe, PageDirectoryParentPage);
  644. #else
  645. //
  646. // Do not reference the top level parent page as it belongs to the
  647. // current process (SMSS).
  648. //
  649. MiInitializePfnForOtherProcess (PageDirectoryPage, PointerPpe, 0);
  650. Pfn1 = MI_PFN_ELEMENT (PageDirectoryPage);
  651. Pfn1->u4.PteFrame = 0;
  652. #endif
  653. ASSERT (MI_PFN_ELEMENT(PageDirectoryPage)->u1.WsIndex == 0);
  654. KeFillEntryTb ((PHARDWARE_PTE) PointerPpe,
  655. MiGetVirtualAddressMappedByPte (PointerPpe),
  656. FALSE);
  657. #endif
  658. //
  659. // Initialize the page table page.
  660. //
  661. MiEnsureAvailablePageOrWait (NULL, NULL);
  662. PageColor = MI_GET_PAGE_COLOR_FROM_VA (NULL);
  663. PageTablePage = MiRemoveZeroPageMayReleaseLocks (PageColor, OldIrql);
  664. //
  665. // The global bit is masked off since we need to make sure the TB entry
  666. // is flushed when we switch to a process in a different session space.
  667. //
  668. TempPte.u.Long = ValidKernelPdeLocal.u.Long;
  669. TempPte.u.Hard.PageFrameNumber = PageTablePage;
  670. PointerPde = MiGetPdeAddress ((PVOID)MmSessionSpace);
  671. ASSERT (PointerPde->u.Long == 0);
  672. MI_WRITE_VALID_PTE (PointerPde, TempPte);
  673. #if (_MI_PAGING_LEVELS >= 3)
  674. MiInitializePfnForOtherProcess (PageTablePage, PointerPde, PageDirectoryPage);
  675. #else
  676. //
  677. // This page frame references itself instead of the current (SMSS.EXE)
  678. // page directory as its PteFrame. This allows the current process to
  679. // appear more normal (at least on 32-bit NT). It just means we have
  680. // to treat this page specially during teardown.
  681. //
  682. MiInitializePfnForOtherProcess (PageTablePage, PointerPde, PageTablePage);
  683. #endif
  684. //
  685. // This page is never paged, ensure that its WsIndex stays clear so the
  686. // release of the page is handled correctly.
  687. //
  688. ASSERT (MI_PFN_ELEMENT(PageTablePage)->u1.WsIndex == 0);
  689. Va = (ULONG_PTR)MiGetPteAddress (MmSessionSpace);
  690. KeFillEntryTb ((PHARDWARE_PTE) PointerPde, (PMMPTE)Va, FALSE);
  691. //
  692. // The global bit is masked off since we need to make sure the TB entry
  693. // is flushed when we switch to a process in a different session space.
  694. //
  695. TempPte.u.Long = ValidKernelPteLocal.u.Long;
  696. TempPte.u.Hard.PageFrameNumber = DataPage;
  697. PointerPte = MiGetPteAddress (MmSessionSpace);
  698. MI_WRITE_VALID_PTE (PointerPte, TempPte);
  699. MiInitializePfn (DataPage, PointerPte, 1);
  700. //
  701. // Now do the second data page.
  702. //
  703. TempPte.u.Hard.PageFrameNumber = DataPage2;
  704. MI_WRITE_VALID_PTE (PointerPte + 1, TempPte);
  705. MiInitializePfn (DataPage2, PointerPte + 1, 1);
  706. //
  707. // Now that the data page is mapped, it can be freed as full hierarchy
  708. // teardown in the event of any future failures encountered by this routine.
  709. //
  710. GotUnmappedDataPage = FALSE;
  711. ASSERT (MI_PFN_ELEMENT(DataPage)->u1.WsIndex == 0);
  712. ASSERT (MI_PFN_ELEMENT(DataPage2)->u1.WsIndex == 0);
  713. UNLOCK_PFN (OldIrql);
  714. KeFillEntryTb ((PHARDWARE_PTE) PointerPte,
  715. (PMMPTE)MmSessionSpace,
  716. FALSE);
  717. KeFillEntryTb ((PHARDWARE_PTE) PointerPte + 1,
  718. (PMMPTE)((PCHAR)MmSessionSpace + PAGE_SIZE),
  719. FALSE);
  720. //
  721. // Initialize the new session space data structure.
  722. //
  723. SessionSpace = MmSessionSpace;
  724. MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_INITIAL_PAGETABLE_ALLOC, 1);
  725. MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_INITIAL_PAGE_ALLOC, 1);
  726. SessionSpace->GlobalPteEntry = GlobalMappingPte;
  727. SessionSpace->GlobalVirtualAddress = SessionGlobal;
  728. #if defined(_IA64_)
  729. SessionSpace->PageDirectoryParentPage = PageDirectoryParentPage;
  730. #endif
  731. SessionSpace->ReferenceCount = 1;
  732. SessionSpace->u.LongFlags = 0;
  733. SessionSpace->SessionId = *SessionId;
  734. SessionSpace->LocaleId = PsDefaultSystemLocaleId;
  735. SessionSpace->SessionPageDirectoryIndex = PageTablePage;
  736. SessionSpace->Color = PageColor;
  737. //
  738. // Track the page table page and the data page.
  739. //
  740. MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_NP_SESSION_CREATE, (ULONG)ResidentPages);
  741. SessionSpace->NonPagablePages = ResidentPages;
  742. SessionSpace->CommittedPages = ResidentPages;
  743. #if (_MI_PAGING_LEVELS >= 3)
  744. //
  745. // Initialize the session data page directory entry so trimmers can attach.
  746. //
  747. #if defined(_AMD64_)
  748. PointerPpe = MiGetPxeAddress ((PVOID)MmSessionSpace);
  749. #else
  750. PointerPpe = MiGetPpeAddress ((PVOID)MmSessionSpace);
  751. #endif
  752. SessionSpace->PageDirectory = *PointerPpe;
  753. #else
  754. SessionSpace->PageTables = PageTables;
  755. //
  756. // Load the session data page table entry so that other processes
  757. // can fault in the mapping.
  758. //
  759. SessionSpace->PageTables[PointerPde - MiGetPdeAddress (MmSessionBase)] = *PointerPde;
  760. #endif
  761. //
  762. // This list entry is only referenced while within the
  763. // session space and has session space (not global) addresses.
  764. //
  765. InitializeListHead (&SessionSpace->ImageList);
  766. //
  767. // Initialize the session space pool.
  768. //
  769. Status = MiInitializeSessionPool ();
  770. if (!NT_SUCCESS(Status)) {
  771. goto Failure;
  772. }
  773. PoolInitialized = TRUE;
  774. //
  775. // Initialize the view mapping support - note this must happen after
  776. // initializing session pool.
  777. //
  778. if (MiInitializeSystemSpaceMap (&SessionGlobal->Session) == FALSE) {
  779. goto Failure;
  780. }
  781. MmUnlockPagableImageSection (ExPageLockHandle);
  782. #if defined (_WIN64)
  783. MiInitializeSpecialPool (PagedPoolSession);
  784. #endif
  785. //
  786. // Use the global virtual address rather than the session space virtual
  787. // address to set up fields that need to be globally accessible.
  788. //
  789. InitializeListHead (&SessionGlobal->WsListEntry);
  790. InitializeListHead (&SessionGlobal->ProcessList);
  791. ASSERT (Process->Flags & PS_PROCESS_FLAGS_CREATING_SESSION);
  792. PS_CLEAR_BITS (&Process->Flags, PS_PROCESS_FLAGS_CREATING_SESSION);
  793. ASSERT (Process->Session == NULL);
  794. ASSERT (SessionGlobal->ProcessReferenceToSession == 0);
  795. SessionGlobal->ProcessReferenceToSession = 1;
  796. InterlockedIncrement (&MmSessionDataPages);
  797. return STATUS_SUCCESS;
  798. Failure:
  799. #if (_MI_PAGING_LEVELS < 3)
  800. ExFreePool (PageTables);
  801. #endif
  802. if (GotCommit == TRUE) {
  803. MiReturnCommitment (ResidentPages);
  804. MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_SESSION_CREATE_FAILURE,
  805. ResidentPages);
  806. }
  807. if (GotPages == TRUE) {
  808. #if (_MI_PAGING_LEVELS >= 4)
  809. PointerPxe = MiGetPxeAddress ((PVOID)MmSessionSpace);
  810. ASSERT (PointerPxe->u.Hard.Valid != 0);
  811. #endif
  812. #if (_MI_PAGING_LEVELS >= 3)
  813. PointerPpe = MiGetPpeAddress ((PVOID)MmSessionSpace);
  814. ASSERT (PointerPpe->u.Hard.Valid != 0);
  815. #endif
  816. PointerPde = MiGetPdeAddress (MmSessionSpace);
  817. ASSERT (PointerPde->u.Hard.Valid != 0);
  818. MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_INITIAL_PAGE_FREE_FAIL1, 1);
  819. MM_BUMP_COUNTER (49, ResidentPages + MI_SESSION_SPACE_WORKING_SET_MINIMUM);
  820. MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_INITIAL_PAGETABLE_FREE_FAIL1, 1);
  821. //
  822. // Do not call MiFreeSessionSpaceMap () as the maps cannot have been
  823. // initialized if we are in this path.
  824. //
  825. //
  826. // Free the initial page table page that was allocated for the
  827. // paged pool range (if it has been allocated at this point).
  828. //
  829. MiFreeSessionPoolBitMaps ();
  830. //
  831. // Capture all needed session information now as after sharing
  832. // is disabled below, no references to session space can be made.
  833. //
  834. #if defined(_IA64_)
  835. KeDetachSessionSpace (&MmSessionMapInfo, MmSessionParentTablePage);
  836. #else
  837. MI_WRITE_INVALID_PTE (PointerPte, ZeroKernelPte);
  838. MI_WRITE_INVALID_PTE (PointerPte + 1, ZeroKernelPte);
  839. MI_WRITE_INVALID_PTE (PointerPde, ZeroKernelPte);
  840. #if defined(_AMD64_)
  841. MI_WRITE_INVALID_PTE (PointerPpe, ZeroKernelPte);
  842. MI_WRITE_INVALID_PTE (PointerPxe, ZeroKernelPte);
  843. #endif
  844. #endif
  845. MI_FLUSH_SESSION_TB ();
  846. LOCK_PFN (OldIrql);
  847. //
  848. // Free the session data structure pages.
  849. //
  850. Pfn1 = MI_PFN_ELEMENT (DataPage);
  851. MiDecrementShareAndValidCount (Pfn1->u4.PteFrame);
  852. MI_SET_PFN_DELETED (Pfn1);
  853. MiDecrementShareCountOnly (DataPage);
  854. Pfn1 = MI_PFN_ELEMENT (DataPage2);
  855. MiDecrementShareAndValidCount (Pfn1->u4.PteFrame);
  856. MI_SET_PFN_DELETED (Pfn1);
  857. MiDecrementShareCountOnly (DataPage2);
  858. //
  859. // Free the page table page.
  860. //
  861. Pfn1 = MI_PFN_ELEMENT (PageTablePage);
  862. #if (_MI_PAGING_LEVELS >= 3)
  863. if (PoolInitialized == TRUE) {
  864. ASSERT (Pfn1->u2.ShareCount == 2);
  865. Pfn1->u2.ShareCount -= 1;
  866. }
  867. ASSERT (Pfn1->u2.ShareCount == 1);
  868. MiDecrementShareAndValidCount (Pfn1->u4.PteFrame);
  869. #else
  870. ASSERT (PageTablePage == Pfn1->u4.PteFrame);
  871. if (PoolInitialized == TRUE) {
  872. ASSERT (Pfn1->u2.ShareCount == 3);
  873. Pfn1->u2.ShareCount -= 2;
  874. }
  875. else {
  876. ASSERT (Pfn1->u2.ShareCount == 2);
  877. Pfn1->u2.ShareCount -= 1;
  878. }
  879. #endif
  880. MI_SET_PFN_DELETED (Pfn1);
  881. MiDecrementShareCountOnly (PageTablePage);
  882. #if (_MI_PAGING_LEVELS >= 3)
  883. //
  884. // Free the page directory page.
  885. //
  886. Pfn1 = MI_PFN_ELEMENT (PageDirectoryPage);
  887. MiDecrementShareAndValidCount (Pfn1->u4.PteFrame);
  888. ASSERT (Pfn1->u2.ShareCount == 1);
  889. ASSERT (Pfn1->u3.e2.ReferenceCount == 1);
  890. MI_SET_PFN_DELETED (Pfn1);
  891. MiDecrementShareCountOnly (PageDirectoryPage);
  892. //
  893. // Free the page directory parent page.
  894. //
  895. ASSERT (Pfn1->u4.PteFrame == PageDirectoryParentPage);
  896. Pfn1 = MI_PFN_ELEMENT (PageDirectoryParentPage);
  897. ASSERT (Pfn1->u2.ShareCount == 1);
  898. ASSERT (Pfn1->u3.e2.ReferenceCount == 1);
  899. MI_SET_PFN_DELETED (Pfn1);
  900. MiDecrementShareCountOnly (PageDirectoryParentPage);
  901. #endif
  902. MmResidentAvailablePages += (ResidentPages + MI_SESSION_SPACE_WORKING_SET_MINIMUM);
  903. UNLOCK_PFN (OldIrql);
  904. }
  905. if (GlobalMappingPte != NULL) {
  906. MiReleaseSystemPtes (GlobalMappingPte, 2, SystemPteSpace);
  907. }
  908. if (GotUnmappedDataPage == TRUE) {
  909. LOCK_PFN (OldIrql);
  910. Pfn1 = MI_PFN_ELEMENT (DataPage);
  911. ASSERT (Pfn1->u2.ShareCount == 0);
  912. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  913. Pfn1->u3.e2.ReferenceCount = 1;
  914. Pfn1->OriginalPte.u.Long = MM_DEMAND_ZERO_WRITE_PTE;
  915. MI_SET_PFN_DELETED (Pfn1);
  916. #if DBG
  917. Pfn1->u3.e1.PageLocation = StandbyPageList;
  918. #endif
  919. MiDecrementReferenceCount (DataPage);
  920. Pfn1 = MI_PFN_ELEMENT (DataPage2);
  921. ASSERT (Pfn1->u2.ShareCount == 0);
  922. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  923. Pfn1->u3.e2.ReferenceCount = 1;
  924. Pfn1->OriginalPte.u.Long = MM_DEMAND_ZERO_WRITE_PTE;
  925. MI_SET_PFN_DELETED (Pfn1);
  926. #if DBG
  927. Pfn1->u3.e1.PageLocation = StandbyPageList;
  928. #endif
  929. MiDecrementReferenceCount (DataPage2);
  930. UNLOCK_PFN (OldIrql);
  931. }
  932. MmUnlockPagableImageSection (ExPageLockHandle);
  933. ExAcquireFastMutex (&MiSessionIdMutex);
  934. ASSERT (RtlCheckBit (MiSessionIdBitmap, *SessionId));
  935. RtlClearBit (MiSessionIdBitmap, *SessionId);
  936. ExReleaseFastMutex (&MiSessionIdMutex);
  937. ASSERT (Process->Flags & PS_PROCESS_FLAGS_CREATING_SESSION);
  938. PS_CLEAR_BITS (&Process->Flags, PS_PROCESS_FLAGS_CREATING_SESSION);
  939. return STATUS_NO_MEMORY;
  940. }
  941. VOID
  942. MiIncrementSessionCount (
  943. VOID
  944. )
  945. /*++
  946. Routine Description:
  947. Nonpaged wrapper to increment the session count. A spinlock is used to
  948. provide serialization with win32k callouts.
  949. Arguments:
  950. None.
  951. Return Value:
  952. None.
  953. --*/
  954. {
  955. KIRQL OldIrql;
  956. LOCK_EXPANSION (OldIrql);
  957. MiSessionCount += 1;
  958. UNLOCK_EXPANSION (OldIrql);
  959. }
  960. LONG MiSessionLeaderExists;
  961. NTSTATUS
  962. MmSessionCreate (
  963. OUT PULONG SessionId
  964. )
  965. /*++
  966. Routine Description:
  967. Called from NtSetSystemInformation() to create a session space
  968. in the calling process with the specified SessionId. An error is returned
  969. if the calling process already has a session space.
  970. Arguments:
  971. SessionId - Supplies a pointer to place the resulting session id in.
  972. Return Value:
  973. Various NTSTATUS error codes.
  974. Environment:
  975. Kernel mode, no mutexes held.
  976. --*/
  977. {
  978. ULONG SessionLeaderExists;
  979. PKTHREAD CurrentThread;
  980. NTSTATUS Status;
  981. PEPROCESS CurrentProcess;
  982. #if DBG && (_MI_PAGING_LEVELS < 3)
  983. PMMPTE StartPde;
  984. PMMPTE EndPde;
  985. #endif
  986. CurrentThread = KeGetCurrentThread ();
  987. ASSERT ((PETHREAD)CurrentThread == PsGetCurrentThread ());
  988. CurrentProcess = PsGetCurrentProcessByThread ((PETHREAD)CurrentThread);
  989. //
  990. // A simple check to see if the calling process already has a session space.
  991. // No need to go through all this if it does. Creation races are caught
  992. // below and recovered from regardless.
  993. //
  994. if (CurrentProcess->Flags & PS_PROCESS_FLAGS_IN_SESSION) {
  995. return STATUS_ALREADY_COMMITTED;
  996. }
  997. if (CurrentProcess->Vm.Flags.SessionLeader == 0) {
  998. //
  999. // Only the session manager can create a session. Make the current
  1000. // process the session leader if this is the first session creation
  1001. // ever.
  1002. //
  1003. // Make sure the add is only done once as this is called multiple times.
  1004. //
  1005. SessionLeaderExists = InterlockedCompareExchange (&MiSessionLeaderExists, 1, 0);
  1006. if (SessionLeaderExists != 0) {
  1007. return STATUS_INVALID_SYSTEM_SERVICE;
  1008. }
  1009. MiSessionLeader (CurrentProcess);
  1010. }
  1011. ASSERT (MmIsAddressValid(MmSessionSpace) == FALSE);
  1012. #if defined (_AMD64_)
  1013. ASSERT ((MiGetPxeAddress(MmSessionBase))->u.Long == ZeroKernelPte.u.Long);
  1014. #endif
  1015. #if (_MI_PAGING_LEVELS < 3)
  1016. #if DBG
  1017. StartPde = MiGetPdeAddress (MmSessionBase);
  1018. EndPde = MiGetPdeAddress (MiSessionSpaceEnd);
  1019. while (StartPde < EndPde) {
  1020. ASSERT (StartPde->u.Long == ZeroKernelPte.u.Long);
  1021. StartPde += 1;
  1022. }
  1023. #endif
  1024. #endif
  1025. KeEnterCriticalRegionThread (CurrentThread);
  1026. Status = MiSessionCreateInternal (SessionId);
  1027. if (!NT_SUCCESS(Status)) {
  1028. KeLeaveCriticalRegionThread (CurrentThread);
  1029. return Status;
  1030. }
  1031. MiIncrementSessionCount ();
  1032. //
  1033. // Add the session space to the working set list.
  1034. //
  1035. Status = MiSessionInitializeWorkingSetList ();
  1036. if (!NT_SUCCESS(Status)) {
  1037. MiDereferenceSession ();
  1038. KeLeaveCriticalRegionThread (CurrentThread);
  1039. return Status;
  1040. }
  1041. KeLeaveCriticalRegionThread (CurrentThread);
  1042. MmSessionSpace->u.Flags.Initialized = 1;
  1043. PS_SET_BITS (&CurrentProcess->Flags, PS_PROCESS_FLAGS_IN_SESSION);
  1044. if (MiSessionLeaderExists == 1) {
  1045. InterlockedCompareExchange (&MiSessionLeaderExists, 2, 1);
  1046. }
  1047. return Status;
  1048. }
  1049. NTSTATUS
  1050. MmSessionDelete (
  1051. ULONG SessionId
  1052. )
  1053. /*++
  1054. Routine Description:
  1055. Called from NtSetSystemInformation() to detach from an existing
  1056. session space in the calling process. An error is returned
  1057. if the calling process has no session space.
  1058. Arguments:
  1059. SessionId - Supplies the session id to delete.
  1060. Return Value:
  1061. STATUS_SUCCESS on success, STATUS_UNABLE_TO_FREE_VM on failure.
  1062. This process will not be able to access session space anymore upon
  1063. a successful return. If this is the last process in the session then
  1064. the entire session is torn down.
  1065. Environment:
  1066. Kernel mode, no mutexes held.
  1067. --*/
  1068. {
  1069. PKTHREAD CurrentThread;
  1070. PEPROCESS CurrentProcess;
  1071. CurrentThread = KeGetCurrentThread ();
  1072. ASSERT ((PETHREAD)CurrentThread == PsGetCurrentThread ());
  1073. CurrentProcess = PsGetCurrentProcessByThread ((PETHREAD)CurrentThread);
  1074. //
  1075. // See if the calling process has a session space - this must be
  1076. // checked since we can be called via a system service.
  1077. //
  1078. if ((CurrentProcess->Flags & PS_PROCESS_FLAGS_IN_SESSION) == 0) {
  1079. #if DBG
  1080. DbgPrint ("MmSessionDelete: Process %p not in a session\n",
  1081. CurrentProcess);
  1082. DbgBreakPoint();
  1083. #endif
  1084. return STATUS_UNABLE_TO_FREE_VM;
  1085. }
  1086. if (CurrentProcess->Vm.Flags.SessionLeader == 0) {
  1087. //
  1088. // Only the session manager can delete a session. This is because
  1089. // it affects the virtual mappings for all threads within the process
  1090. // when this address space is deleted. This is different from normal
  1091. // VAD clearing because win32k and other drivers rely on this space.
  1092. //
  1093. return STATUS_UNABLE_TO_FREE_VM;
  1094. }
  1095. ASSERT (MmIsAddressValid(MmSessionSpace) == TRUE);
  1096. if (MmSessionSpace->SessionId != SessionId) {
  1097. #if DBG
  1098. DbgPrint("MmSessionDelete: Wrong SessionId! Own %d, Ask %d\n",
  1099. MmSessionSpace->SessionId,
  1100. SessionId);
  1101. DbgBreakPoint();
  1102. #endif
  1103. return STATUS_UNABLE_TO_FREE_VM;
  1104. }
  1105. KeEnterCriticalRegionThread (CurrentThread);
  1106. MiDereferenceSession ();
  1107. KeLeaveCriticalRegionThread (CurrentThread);
  1108. return STATUS_SUCCESS;
  1109. }
  1110. VOID
  1111. MiAttachSession (
  1112. IN PMM_SESSION_SPACE SessionGlobal
  1113. )
  1114. /*++
  1115. Routine Description:
  1116. Attaches to the specified session space.
  1117. Arguments:
  1118. SessionGlobal - Supplies a pointer to the session to attach to.
  1119. Return Value:
  1120. None.
  1121. Environment:
  1122. Kernel mode. No locks held. Current process must not have a session
  1123. space - ie: the caller should be the system process or smss.exe.
  1124. --*/
  1125. {
  1126. PMMPTE PointerPde;
  1127. ASSERT ((PsGetCurrentProcess()->Flags & PS_PROCESS_FLAGS_IN_SESSION) == 0);
  1128. #if defined (_AMD64_)
  1129. PointerPde = MiGetPxeAddress (MmSessionBase);
  1130. ASSERT (PointerPde->u.Long == ZeroKernelPte.u.Long);
  1131. MI_WRITE_VALID_PTE (PointerPde, SessionGlobal->PageDirectory);
  1132. #elif defined(_IA64_)
  1133. PointerPde = (PMMPTE) (&PsGetCurrentProcess()->Pcb.SessionParentBase);
  1134. ASSERT (MI_GET_PAGE_FRAME_FROM_PTE(PointerPde) == MmSessionParentTablePage);
  1135. KeAttachSessionSpace (&SessionGlobal->SessionMapInfo,
  1136. SessionGlobal->PageDirectoryParentPage);
  1137. #else
  1138. PointerPde = MiGetPdeAddress (MmSessionBase);
  1139. ASSERT (RtlCompareMemoryUlong (PointerPde,
  1140. MiSessionSpacePageTables * sizeof (MMPTE),
  1141. 0) == MiSessionSpacePageTables * sizeof (MMPTE));
  1142. RtlCopyMemory (PointerPde,
  1143. &SessionGlobal->PageTables[0],
  1144. MiSessionSpacePageTables * sizeof (MMPTE));
  1145. #endif
  1146. }
  1147. VOID
  1148. MiDetachSession (
  1149. VOID
  1150. )
  1151. /*++
  1152. Routine Description:
  1153. Detaches from the specified session space.
  1154. Arguments:
  1155. None.
  1156. Return Value:
  1157. None.
  1158. Environment:
  1159. Kernel mode. No locks held. Current process must not have a session
  1160. space to return to - ie: this should be the system process.
  1161. --*/
  1162. {
  1163. PMMPTE PointerPde;
  1164. ASSERT ((PsGetCurrentProcess()->Flags & PS_PROCESS_FLAGS_IN_SESSION) == 0);
  1165. ASSERT (MmIsAddressValid(MmSessionSpace) == TRUE);
  1166. #if defined (_AMD64_)
  1167. PointerPde = MiGetPxeAddress (MmSessionBase);
  1168. PointerPde->u.Long = ZeroKernelPte.u.Long;
  1169. #elif defined(_IA64_)
  1170. PointerPde = (PMMPTE) (&PsGetCurrentProcess()->Pcb.SessionParentBase);
  1171. ASSERT (MI_GET_PAGE_FRAME_FROM_PTE(PointerPde) == MmSessionSpace->PageDirectoryParentPage);
  1172. KeDetachSessionSpace (&MmSessionMapInfo, MmSessionParentTablePage);
  1173. #else
  1174. PointerPde = MiGetPdeAddress (MmSessionBase);
  1175. RtlZeroMemory (PointerPde, MiSessionSpacePageTables * sizeof (MMPTE));
  1176. #endif
  1177. MI_FLUSH_SESSION_TB ();
  1178. }
  1179. #if DBG
  1180. VOID
  1181. MiCheckSessionVirtualSpace (
  1182. IN PVOID VirtualAddress,
  1183. IN SIZE_T NumberOfBytes
  1184. )
  1185. /*++
  1186. Routine Description:
  1187. Used to verify that no drivers fail to clean up their session allocations.
  1188. Arguments:
  1189. VirtualAddress - Supplies the starting virtual address to check.
  1190. NumberOfBytes - Supplies the number of bytes to check.
  1191. Return Value:
  1192. TRUE if all the PTEs have been freed, FALSE if not.
  1193. Environment:
  1194. Kernel mode. APCs disabled.
  1195. --*/
  1196. {
  1197. PMMPTE StartPde;
  1198. PMMPTE EndPde;
  1199. PMMPTE StartPte;
  1200. PMMPTE EndPte;
  1201. ULONG Index;
  1202. //
  1203. // Check the specified region. Everything should have been cleaned up
  1204. // already.
  1205. //
  1206. #if defined (_AMD64_)
  1207. ASSERT64 (MiGetPxeAddress (VirtualAddress)->u.Hard.Valid == 1);
  1208. #endif
  1209. ASSERT64 (MiGetPpeAddress (VirtualAddress)->u.Hard.Valid == 1);
  1210. StartPde = MiGetPdeAddress (VirtualAddress);
  1211. EndPde = MiGetPdeAddress ((PVOID)((PCHAR)VirtualAddress + NumberOfBytes - 1));
  1212. StartPte = MiGetPteAddress (VirtualAddress);
  1213. EndPte = MiGetPteAddress ((PVOID)((PCHAR)VirtualAddress + NumberOfBytes - 1));
  1214. Index = (ULONG)(StartPde - MiGetPdeAddress ((PVOID)MmSessionBase));
  1215. #if (_MI_PAGING_LEVELS >= 3)
  1216. while (StartPde <= EndPde && StartPde->u.Long == 0)
  1217. #else
  1218. while (StartPde <= EndPde && MmSessionSpace->PageTables[Index].u.Long == 0)
  1219. #endif
  1220. {
  1221. StartPde += 1;
  1222. Index += 1;
  1223. StartPte = MiGetVirtualAddressMappedByPte (StartPde);
  1224. }
  1225. while (StartPte <= EndPte) {
  1226. if (MiIsPteOnPdeBoundary(StartPte)) {
  1227. StartPde = MiGetPteAddress (StartPte);
  1228. Index = (ULONG)(StartPde - MiGetPdeAddress ((PVOID)MmSessionBase));
  1229. #if (_MI_PAGING_LEVELS >= 3)
  1230. while (StartPde <= EndPde && StartPde->u.Long == 0)
  1231. #else
  1232. while (StartPde <= EndPde && MmSessionSpace->PageTables[Index].u.Long == 0)
  1233. #endif
  1234. {
  1235. Index += 1;
  1236. StartPde += 1;
  1237. StartPte = MiGetVirtualAddressMappedByPte (StartPde);
  1238. }
  1239. if (StartPde > EndPde) {
  1240. break;
  1241. }
  1242. }
  1243. if (StartPte->u.Long != 0 && StartPte->u.Long != MM_KERNEL_NOACCESS_PTE) {
  1244. DbgPrint("MiCheckSessionVirtualSpace: StartPte 0x%p is still valid! 0x%p, VA 0x%p\n",
  1245. StartPte,
  1246. StartPte->u.Long,
  1247. MiGetVirtualAddressMappedByPte(StartPte));
  1248. DbgBreakPoint();
  1249. }
  1250. StartPte += 1;
  1251. }
  1252. }
  1253. #endif
  1254. VOID
  1255. MiSessionDeletePde (
  1256. IN PMMPTE Pde,
  1257. IN LOGICAL WorkingSetInitialized,
  1258. IN PMMPTE SelfMapPde
  1259. )
  1260. /*++
  1261. Routine Description:
  1262. Used to delete a page directory entry from a session space.
  1263. Arguments:
  1264. Pde - Supplies the page directory entry to delete.
  1265. WorkingSetInitialized - Supplies TRUE if the working set has been
  1266. initialized.
  1267. SelfMapPde - Supplies the page directory entry that contains the self map
  1268. session page.
  1269. Return Value:
  1270. None.
  1271. Environment:
  1272. Kernel mode. PFN lock held.
  1273. --*/
  1274. {
  1275. PMMPFN Pfn1;
  1276. PFN_NUMBER PageFrameIndex;
  1277. LOGICAL SelfMapPage;
  1278. #if DBG
  1279. PMMPFN Pfn2;
  1280. #else
  1281. UNREFERENCED_PARAMETER (WorkingSetInitialized);
  1282. #endif
  1283. if (Pde->u.Long == ZeroKernelPte.u.Long) {
  1284. return;
  1285. }
  1286. SelfMapPage = (Pde == SelfMapPde ? TRUE : FALSE);
  1287. ASSERT (Pde->u.Hard.Valid == 1);
  1288. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (Pde);
  1289. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  1290. #if DBG
  1291. ASSERT (PageFrameIndex <= MmHighestPhysicalPage);
  1292. if (WorkingSetInitialized == TRUE) {
  1293. ASSERT (Pfn1->u1.WsIndex);
  1294. }
  1295. ASSERT (Pfn1->u3.e1.PrototypePte == 0);
  1296. ASSERT (Pfn1->u3.e2.ReferenceCount == 1);
  1297. ASSERT (Pfn1->u4.PteFrame <= MmHighestPhysicalPage);
  1298. Pfn2 = MI_PFN_ELEMENT (Pfn1->u4.PteFrame);
  1299. //
  1300. // Verify the containing page table is still the page
  1301. // table page mapping the session data structure.
  1302. //
  1303. if (SelfMapPage == FALSE) {
  1304. //
  1305. // Note these ASSERTs will fail if win32k leaks pool.
  1306. //
  1307. ASSERT (Pfn1->u2.ShareCount == 1);
  1308. //
  1309. // NT32 points the additional page tables at the master.
  1310. // NT64 doesn't need to use this trick as there is always
  1311. // an additional hierarchy level.
  1312. //
  1313. ASSERT32 (Pfn1->u4.PteFrame == MI_GET_PAGE_FRAME_FROM_PTE (SelfMapPde));
  1314. ASSERT32 (Pfn2->u2.ShareCount >= 2);
  1315. }
  1316. else {
  1317. ASSERT32 (Pfn1 == Pfn2);
  1318. ASSERT32 (Pfn1->u2.ShareCount == 2);
  1319. ASSERT64 (Pfn1->u2.ShareCount == 1);
  1320. }
  1321. #endif // DBG
  1322. if (SelfMapPage == FALSE) {
  1323. MiDecrementShareAndValidCount (Pfn1->u4.PteFrame);
  1324. }
  1325. MI_SET_PFN_DELETED (Pfn1);
  1326. MiDecrementShareCountOnly (PageFrameIndex);
  1327. }
  1328. VOID
  1329. MiReleaseProcessReferenceToSessionDataPage (
  1330. PMM_SESSION_SPACE SessionGlobal
  1331. )
  1332. /*++
  1333. Routine Description:
  1334. Decrement this process' session reference. The session itself may have
  1335. already been deleted. If this is the last reference to the session,
  1336. then the session data page and its mapping PTE (if any) will be destroyed
  1337. upon return.
  1338. Arguments:
  1339. SessionGlobal - Supplies the global session space pointer being
  1340. dereferenced. The caller has already verified that this
  1341. process is a member of the target session.
  1342. Return Value:
  1343. None.
  1344. Environment:
  1345. Kernel mode, no mutexes held, APCs disabled.
  1346. --*/
  1347. {
  1348. ULONG SessionId;
  1349. PMMPTE GlobalPteEntrySave;
  1350. PFN_NUMBER PageFrameIndex;
  1351. PFN_NUMBER PageFrameIndex2;
  1352. PMMPFN Pfn1;
  1353. PMMPFN Pfn2;
  1354. KIRQL OldIrql;
  1355. if (InterlockedDecrement (&SessionGlobal->ProcessReferenceToSession) != 0) {
  1356. return;
  1357. }
  1358. SessionId = SessionGlobal->SessionId;
  1359. //
  1360. // Free the datapages & self-map PTE now since this is the last
  1361. // process reference and KeDetach has returned.
  1362. //
  1363. #if (_MI_PAGING_LEVELS < 3)
  1364. ExFreePool (SessionGlobal->PageTables);
  1365. #endif
  1366. GlobalPteEntrySave = SessionGlobal->GlobalPteEntry;
  1367. ASSERT (!MI_IS_PHYSICAL_ADDRESS(SessionGlobal));
  1368. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (GlobalPteEntrySave);
  1369. PageFrameIndex2 = MI_GET_PAGE_FRAME_FROM_PTE (GlobalPteEntrySave + 1);
  1370. MiReleaseSystemPtes (GlobalPteEntrySave, 2, SystemPteSpace);
  1371. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  1372. Pfn2 = MI_PFN_ELEMENT (PageFrameIndex2);
  1373. LOCK_PFN (OldIrql);
  1374. ASSERT (Pfn1->u2.ShareCount == 1);
  1375. ASSERT (Pfn1->u3.e2.ReferenceCount == 1);
  1376. MI_SET_PFN_DELETED (Pfn1);
  1377. MiDecrementShareCountOnly (PageFrameIndex);
  1378. //
  1379. // Free the second datapage.
  1380. //
  1381. ASSERT (Pfn2->u2.ShareCount == 1);
  1382. ASSERT (Pfn2->u3.e2.ReferenceCount == 1);
  1383. MI_SET_PFN_DELETED (Pfn2);
  1384. MiDecrementShareCountOnly (PageFrameIndex2);
  1385. MmResidentAvailablePages += 2;
  1386. MM_BUMP_COUNTER(52, 2);
  1387. UNLOCK_PFN (OldIrql);
  1388. InterlockedDecrement (&MmSessionDataPages);
  1389. //
  1390. // Return commitment for the datapages.
  1391. //
  1392. MiReturnCommitment (2);
  1393. MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_SESSION_DATAPAGE, 2);
  1394. //
  1395. // Release the session ID so it can be recycled.
  1396. //
  1397. ExAcquireFastMutex (&MiSessionIdMutex);
  1398. ASSERT (RtlCheckBit (MiSessionIdBitmap, SessionId));
  1399. RtlClearBit (MiSessionIdBitmap, SessionId);
  1400. ExReleaseFastMutex (&MiSessionIdMutex);
  1401. }
  1402. VOID
  1403. MiDereferenceSession (
  1404. VOID
  1405. )
  1406. /*++
  1407. Routine Description:
  1408. Decrement this process' reference count to the session, unmapping access
  1409. to the session for the current process. If this is the last process
  1410. reference to this session, then the entire session will be destroyed upon
  1411. return. This includes unloading drivers, unmapping pools, freeing
  1412. page tables, etc.
  1413. Arguments:
  1414. None.
  1415. Return Value:
  1416. None.
  1417. Environment:
  1418. Kernel mode, no mutexes held, APCs disabled.
  1419. --*/
  1420. {
  1421. #if !defined(_IA64_)
  1422. PMMPTE StartPde;
  1423. #endif
  1424. ULONG SessionId;
  1425. PEPROCESS Process;
  1426. PMM_SESSION_SPACE SessionGlobal;
  1427. ASSERT ((PsGetCurrentProcess()->Flags & PS_PROCESS_FLAGS_IN_SESSION) ||
  1428. ((MmSessionSpace->u.Flags.Initialized == 0) && (PsGetCurrentProcess()->Vm.Flags.SessionLeader == 1) && (MmSessionSpace->ReferenceCount == 1)));
  1429. SessionId = MmSessionSpace->SessionId;
  1430. ASSERT (RtlCheckBit (MiSessionIdBitmap, SessionId));
  1431. if (InterlockedDecrement ((PLONG)&MmSessionSpace->ReferenceCount) != 0) {
  1432. Process = PsGetCurrentProcess ();
  1433. PS_CLEAR_BITS (&Process->Flags, PS_PROCESS_FLAGS_IN_SESSION);
  1434. //
  1435. // Don't delete any non-smss session space mappings here. Let them
  1436. // live on through process death. This handles the case where
  1437. // MmDispatchWin32Callout picks csrss - csrss has exited as it's not
  1438. // the last process (smss is). smss is simultaneously detaching from
  1439. // the session and since it is the last process, it's waiting on
  1440. // the AttachCount below. The dispatch callout ends up in csrss but
  1441. // has no way to synchronize against csrss exiting through this path
  1442. // as the object reference count doesn't stop it. So leave the
  1443. // session space mappings alive so the callout can execute through
  1444. // the remains of csrss.
  1445. //
  1446. // Note that when smss detaches, the address space must get cleared
  1447. // here so that subsequent session creations by smss will succeed.
  1448. //
  1449. if (Process->Vm.Flags.SessionLeader == 1) {
  1450. SessionGlobal = SESSION_GLOBAL (MmSessionSpace);
  1451. #if defined(_IA64_)
  1452. //
  1453. // Revert back to the pre-session dummy top level page.
  1454. //
  1455. KeDetachSessionSpace (&MmSessionMapInfo, MmSessionParentTablePage);
  1456. #elif defined (_AMD64_)
  1457. StartPde = MiGetPxeAddress (MmSessionBase);
  1458. StartPde->u.Long = ZeroKernelPte.u.Long;
  1459. #else
  1460. StartPde = MiGetPdeAddress (MmSessionBase);
  1461. RtlZeroMemory (StartPde, MiSessionSpacePageTables * sizeof(MMPTE));
  1462. #endif
  1463. MI_FLUSH_SESSION_TB ();
  1464. //
  1465. // This process' reference to the session must be NULL as the
  1466. // KeDetach has completed so no swap context referencing the
  1467. // earlier session page can occur from here on. This is also
  1468. // needed because during clean shutdowns, the final dereference
  1469. // of this process (smss) object will trigger an
  1470. // MmDeleteProcessAddressSpace - this routine will dereference
  1471. // the (no-longer existing) session space if this
  1472. // bit is not cleared properly.
  1473. //
  1474. ASSERT (Process->Session == NULL);
  1475. //
  1476. // Another process may have won the race and exited the session
  1477. // as this process is executing here. Hence the reference count
  1478. // is carefully checked here to ensure no leaks occur.
  1479. //
  1480. MiReleaseProcessReferenceToSessionDataPage (SessionGlobal);
  1481. }
  1482. return;
  1483. }
  1484. //
  1485. // This is the final process in the session so the entire session must
  1486. // be dereferenced now.
  1487. //
  1488. MiDereferenceSessionFinal ();
  1489. }
  1490. VOID
  1491. MiDereferenceSessionFinal (
  1492. VOID
  1493. )
  1494. /*++
  1495. Routine Description:
  1496. Decrement this process' reference count to the session, unmapping access
  1497. to the session for the current process. If this is the last process
  1498. reference to this session, then the entire session will be destroyed upon
  1499. return. This includes unloading drivers, unmapping pools, freeing
  1500. page tables, etc.
  1501. Arguments:
  1502. None.
  1503. Return Value:
  1504. None.
  1505. Environment:
  1506. Kernel mode, no mutexes held, APCs disabled.
  1507. --*/
  1508. {
  1509. KIRQL OldIrql;
  1510. ULONG Index;
  1511. ULONG_PTR CountReleased;
  1512. ULONG_PTR CountReleased2;
  1513. ULONG SessionId;
  1514. PFN_NUMBER PageFrameIndex;
  1515. PFN_NUMBER PageFrameIndex2;
  1516. ULONG SessionDataPdeIndex;
  1517. KEVENT Event;
  1518. PMMPFN Pfn1;
  1519. PMMPFN Pfn2;
  1520. PMMPTE PointerPte;
  1521. PMMPTE EndPte;
  1522. PMMPTE GlobalPteEntrySave;
  1523. PMMPTE StartPde;
  1524. PMM_SESSION_SPACE SessionGlobal;
  1525. LOGICAL WorkingSetWasInitialized;
  1526. ULONG AttachCount;
  1527. PEPROCESS Process;
  1528. PKTHREAD CurrentThread;
  1529. #if (_MI_PAGING_LEVELS >= 3)
  1530. PFN_NUMBER PageDirectoryFrame;
  1531. PFN_NUMBER PageParentFrame;
  1532. MMPTE SavePageTables[MI_SESSION_SPACE_MAXIMUM_PAGE_TABLES];
  1533. #endif
  1534. Process = PsGetCurrentProcess();
  1535. ASSERT ((Process->Flags & PS_PROCESS_FLAGS_IN_SESSION) ||
  1536. ((MmSessionSpace->u.Flags.Initialized == 0) && (Process->Vm.Flags.SessionLeader == 1) && (MmSessionSpace->ReferenceCount == 0)));
  1537. SessionId = MmSessionSpace->SessionId;
  1538. ASSERT (RtlCheckBit (MiSessionIdBitmap, SessionId));
  1539. //
  1540. // This is the final dereference. We could be any process
  1541. // including SMSS when a session space load fails. Note also that
  1542. // processes can terminate in any order as well.
  1543. //
  1544. SessionGlobal = SESSION_GLOBAL (MmSessionSpace);
  1545. MmLockPagableSectionByHandle (ExPageLockHandle);
  1546. LOCK_EXPANSION (OldIrql);
  1547. //
  1548. // Wait for any cross-session process attaches to detach. Refuse
  1549. // subsequent attempts to cross-session attach so the address invalidation
  1550. // code doesn't surprise an ongoing or subsequent attachee.
  1551. //
  1552. ASSERT (MmSessionSpace->u.Flags.DeletePending == 0);
  1553. MmSessionSpace->u.Flags.DeletePending = 1;
  1554. AttachCount = MmSessionSpace->AttachCount;
  1555. if (AttachCount) {
  1556. KeInitializeEvent (&SessionGlobal->AttachEvent,
  1557. NotificationEvent,
  1558. FALSE);
  1559. UNLOCK_EXPANSION (OldIrql);
  1560. KeWaitForSingleObject( &SessionGlobal->AttachEvent,
  1561. WrVirtualMemory,
  1562. KernelMode,
  1563. FALSE,
  1564. (PLARGE_INTEGER)NULL);
  1565. LOCK_EXPANSION (OldIrql);
  1566. ASSERT (MmSessionSpace->u.Flags.DeletePending == 1);
  1567. ASSERT (MmSessionSpace->AttachCount == 0);
  1568. }
  1569. if (MmSessionSpace->Vm.Flags.BeingTrimmed) {
  1570. //
  1571. // Initialize an event and put the event address
  1572. // in the VmSupport. When the trimming is complete,
  1573. // this event will be set.
  1574. //
  1575. KeInitializeEvent(&Event, NotificationEvent, FALSE);
  1576. MmSessionSpace->Vm.WorkingSetExpansionLinks.Blink = (PLIST_ENTRY)&Event;
  1577. //
  1578. // Release the mutex and wait for the event.
  1579. //
  1580. CurrentThread = KeGetCurrentThread ();
  1581. KeEnterCriticalRegionThread (CurrentThread);
  1582. UNLOCK_EXPANSION_AND_THEN_WAIT (OldIrql);
  1583. KeWaitForSingleObject(&Event,
  1584. WrVirtualMemory,
  1585. KernelMode,
  1586. FALSE,
  1587. (PLARGE_INTEGER)NULL);
  1588. KeLeaveCriticalRegionThread (CurrentThread);
  1589. LOCK_EXPANSION (OldIrql);
  1590. }
  1591. else if (MmSessionSpace->u.Flags.WorkingSetInserted == 1) {
  1592. //
  1593. // Remove this session from the session list and the working
  1594. // set list.
  1595. //
  1596. RemoveEntryList (&SessionGlobal->Vm.WorkingSetExpansionLinks);
  1597. MmSessionSpace->u.Flags.WorkingSetInserted = 0;
  1598. }
  1599. if (MmSessionSpace->u.Flags.SessionListInserted == 1) {
  1600. RemoveEntryList (&SessionGlobal->WsListEntry);
  1601. MmSessionSpace->u.Flags.SessionListInserted = 0;
  1602. }
  1603. MiSessionCount -= 1;
  1604. UNLOCK_EXPANSION (OldIrql);
  1605. #if DBG
  1606. if (Process->Vm.Flags.SessionLeader == 0) {
  1607. ASSERT (MmSessionSpace->ProcessOutSwapCount == 0);
  1608. ASSERT (MmSessionSpace->ReferenceCount == 0);
  1609. }
  1610. #endif
  1611. MM_SNAP_SESS_MEMORY_COUNTERS(0);
  1612. //
  1613. // If an unload function has been registered for WIN32K.SYS,
  1614. // call it now before we force an unload on any modules. WIN32K.SYS
  1615. // is responsible for calling any other loaded modules that have
  1616. // unload routines to be run. Another option is to have the other
  1617. // session drivers register a DLL initialize/uninitialize pair on load.
  1618. //
  1619. if (MmSessionSpace->Win32KDriverObject.DriverUnload) {
  1620. MmSessionSpace->Win32KDriverObject.DriverUnload (&MmSessionSpace->Win32KDriverObject);
  1621. }
  1622. //
  1623. // Complete all deferred pool block deallocations.
  1624. //
  1625. ExDeferredFreePool (&MmSessionSpace->PagedPool);
  1626. //
  1627. // Now that all modules have had their unload routine(s)
  1628. // called, check for pool leaks before unloading the images.
  1629. //
  1630. MiCheckSessionPoolAllocations ();
  1631. ASSERT (MmSessionSpace->ReferenceCount == 0);
  1632. #if defined (_WIN64)
  1633. if (MmSessionSpecialPoolStart != 0) {
  1634. MiDeleteSessionSpecialPool ();
  1635. }
  1636. #endif
  1637. MM_SNAP_SESS_MEMORY_COUNTERS(1);
  1638. //
  1639. // Destroy the view mapping structures.
  1640. //
  1641. MiFreeSessionSpaceMap ();
  1642. MM_SNAP_SESS_MEMORY_COUNTERS(2);
  1643. //
  1644. // Walk down the list of modules we have loaded dereferencing them.
  1645. //
  1646. // This allows us to force an unload of any kernel images loaded by
  1647. // the session so we do not have any virtual space and paging
  1648. // file leaks.
  1649. //
  1650. MiSessionUnloadAllImages ();
  1651. MM_SNAP_SESS_MEMORY_COUNTERS(3);
  1652. //
  1653. // Destroy the session space bitmap structure
  1654. //
  1655. MiFreeSessionPoolBitMaps ();
  1656. MM_SNAP_SESS_MEMORY_COUNTERS(4);
  1657. //
  1658. // Reference the session space structure using its global
  1659. // kernel PTE based address. This is to avoid deleting it out
  1660. // from underneath ourselves.
  1661. //
  1662. GlobalPteEntrySave = MmSessionSpace->GlobalPteEntry;
  1663. ASSERT (GlobalPteEntrySave != NULL);
  1664. //
  1665. // Sweep the individual regions in their proper order.
  1666. //
  1667. #if DBG
  1668. //
  1669. // Check the executable image region. All images
  1670. // should have been unloaded by the image handler.
  1671. //
  1672. MiCheckSessionVirtualSpace ((PVOID) MiSessionImageStart,
  1673. MiSessionImageEnd - MiSessionImageStart);
  1674. #endif
  1675. MM_SNAP_SESS_MEMORY_COUNTERS(5);
  1676. #if DBG
  1677. //
  1678. // Check the view region. All views should have been cleaned up already.
  1679. //
  1680. MiCheckSessionVirtualSpace ((PVOID) MiSessionViewStart, MmSessionViewSize);
  1681. #endif
  1682. #if (_MI_PAGING_LEVELS >= 3)
  1683. RtlCopyMemory (SavePageTables,
  1684. MiGetPdeAddress ((PVOID)MmSessionBase),
  1685. MiSessionSpacePageTables * sizeof (MMPTE));
  1686. #endif
  1687. MM_SNAP_SESS_MEMORY_COUNTERS(6);
  1688. #if DBG
  1689. //
  1690. // Check everything possible before the remaining virtual address space
  1691. // is torn down. In this way if anything is amiss, the data can be
  1692. // more easily examined.
  1693. //
  1694. Pfn1 = MI_PFN_ELEMENT (MmSessionSpace->SessionPageDirectoryIndex);
  1695. //
  1696. // This should be greater than 1 because working set page tables are
  1697. // using this as their parent as well.
  1698. //
  1699. ASSERT (Pfn1->u2.ShareCount > 1);
  1700. #endif
  1701. CountReleased = 0;
  1702. if (MmSessionSpace->u.Flags.HasWsLock == 1) {
  1703. PointerPte = MiGetPteAddress ((PVOID)MiSessionSpaceWs);
  1704. EndPte = MiGetPteAddress (MmSessionSpace->Vm.VmWorkingSetList->HighestPermittedHashAddress);
  1705. for ( ; PointerPte < EndPte; PointerPte += 1) {
  1706. if (PointerPte->u.Long) {
  1707. ASSERT (PointerPte->u.Hard.Valid == 1);
  1708. CountReleased += 1;
  1709. }
  1710. }
  1711. MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_WS_PAGE_FREE, (ULONG) CountReleased);
  1712. InterlockedExchangeAddSizeT (&MmSessionSpace->CommittedPages,
  1713. 0 - CountReleased);
  1714. MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_NP_WS_PAGE_FREE, (ULONG) CountReleased);
  1715. MmSessionSpace->NonPagablePages -= CountReleased;
  1716. WorkingSetWasInitialized = TRUE;
  1717. MmSessionSpace->u.Flags.HasWsLock = 0;
  1718. }
  1719. else {
  1720. WorkingSetWasInitialized = FALSE;
  1721. }
  1722. //
  1723. // Account for the session data structure data page. For NT64, the page
  1724. // directory page is also accounted for here.
  1725. //
  1726. // Note CountReleased is deliberately incremented by one less than the
  1727. // CommittedPages/NonPagablePages is incremented by.
  1728. // This is because the data page (and its commitment) can only be returned
  1729. // after the last process has been reaped (not just exited).
  1730. //
  1731. #if (_MI_PAGING_LEVELS >= 3)
  1732. MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_INITIAL_PAGE_FREE, 4);
  1733. InterlockedExchangeAddSizeT (&MmSessionSpace->CommittedPages, -4);
  1734. MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_NP_SESSION_DESTROY, 4);
  1735. MmSessionSpace->NonPagablePages -= 4;
  1736. CountReleased += 3;
  1737. #else
  1738. MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_INITIAL_PAGE_FREE, 2);
  1739. InterlockedExchangeAddSizeT (&MmSessionSpace->CommittedPages, -2);
  1740. MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_NP_SESSION_DESTROY, 2);
  1741. MmSessionSpace->NonPagablePages -= 2;
  1742. #endif
  1743. //
  1744. // Account for any needed session space page tables. Note the common
  1745. // structure (not the local PDEs) must be examined as any page tables
  1746. // that were dynamically materialized in the context of a different
  1747. // process may not be in the current process' page directory (ie: the
  1748. // current process has never accessed the materialized VAs) !
  1749. //
  1750. #if (_MI_PAGING_LEVELS >= 3)
  1751. StartPde = MiGetPdeAddress ((PVOID)MmSessionBase);
  1752. #else
  1753. StartPde = &MmSessionSpace->PageTables[0];
  1754. #endif
  1755. CountReleased2 = 0;
  1756. for (Index = 0; Index < MiSessionSpacePageTables; Index += 1) {
  1757. if (StartPde->u.Long != ZeroKernelPte.u.Long) {
  1758. CountReleased2 += 1;
  1759. }
  1760. StartPde += 1;
  1761. }
  1762. MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_PAGETABLE_FREE, (ULONG) CountReleased2);
  1763. InterlockedExchangeAddSizeT (&MmSessionSpace->CommittedPages,
  1764. 0 - CountReleased2);
  1765. MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_NP_SESSION_PTDESTROY, (ULONG) CountReleased2);
  1766. MmSessionSpace->NonPagablePages -= CountReleased2;
  1767. CountReleased += CountReleased2;
  1768. ASSERT (MmSessionSpace->NonPagablePages == 0);
  1769. //
  1770. // Note that whenever win32k or drivers loaded by it leak pool, the
  1771. // ASSERT below will be triggered.
  1772. //
  1773. ASSERT (MmSessionSpace->CommittedPages == 0);
  1774. MiReturnCommitment (CountReleased);
  1775. MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_SESSION_DEREFERENCE, CountReleased);
  1776. //
  1777. // Sweep the working set entries.
  1778. // No more accesses to the working set or its lock are allowed.
  1779. //
  1780. if (WorkingSetWasInitialized == TRUE) {
  1781. ExDeleteResourceLite (&SessionGlobal->WsLock);
  1782. PointerPte = MiGetPteAddress ((PVOID)MiSessionSpaceWs);
  1783. EndPte = MiGetPteAddress (MmSessionSpace->Vm.VmWorkingSetList->HighestPermittedHashAddress);
  1784. for ( ; PointerPte < EndPte; PointerPte += 1) {
  1785. if (PointerPte->u.Long) {
  1786. ASSERT (PointerPte->u.Hard.Valid == 1);
  1787. //
  1788. // Delete the page.
  1789. //
  1790. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
  1791. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  1792. //
  1793. // Each page should still be locked in the session working set.
  1794. //
  1795. LOCK_PFN (OldIrql);
  1796. ASSERT (Pfn1->u3.e2.ReferenceCount == 1);
  1797. MiDecrementShareAndValidCount (Pfn1->u4.PteFrame);
  1798. MI_SET_PFN_DELETED (Pfn1);
  1799. MiDecrementShareCountOnly (PageFrameIndex);
  1800. MI_WRITE_INVALID_PTE (PointerPte, ZeroKernelPte);
  1801. //
  1802. // Don't return the resident available pages charge here
  1803. // as it's going to be returned in one chunk below as part of
  1804. // CountReleased.
  1805. //
  1806. UNLOCK_PFN (OldIrql);
  1807. }
  1808. }
  1809. }
  1810. ASSERT (!MI_IS_PHYSICAL_ADDRESS(SessionGlobal));
  1811. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (GlobalPteEntrySave);
  1812. PageFrameIndex2 = MI_GET_PAGE_FRAME_FROM_PTE (GlobalPteEntrySave + 1);
  1813. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  1814. Pfn2 = MI_PFN_ELEMENT (PageFrameIndex2);
  1815. ASSERT (Pfn1->u4.PteFrame == MmSessionSpace->SessionPageDirectoryIndex);
  1816. ASSERT (Pfn2->u4.PteFrame == MmSessionSpace->SessionPageDirectoryIndex);
  1817. //
  1818. // Make sure the data pages are still locked.
  1819. //
  1820. ASSERT (Pfn1->u1.WsIndex == 0);
  1821. ASSERT (Pfn2->u1.WsIndex == 1);
  1822. #if defined(_IA64_)
  1823. KeDetachSessionSpace (&MmSessionMapInfo, MmSessionParentTablePage);
  1824. #elif defined (_AMD64_)
  1825. StartPde = MiGetPxeAddress (MmSessionBase);
  1826. StartPde->u.Long = ZeroKernelPte.u.Long;
  1827. #else
  1828. StartPde = MiGetPdeAddress (MmSessionBase);
  1829. RtlZeroMemory (StartPde, MiSessionSpacePageTables * sizeof(MMPTE));
  1830. #endif
  1831. //
  1832. // Delete the VA space - no more accesses to MmSessionSpace at this point.
  1833. //
  1834. // Cut off the pagetable reference as the local page table is going to be
  1835. // freed now. Any needed references must go through the global PTE
  1836. // space or superpages but never through the local session VA. The
  1837. // actual session data pages are not freed until the very last process
  1838. // of the session receives its very last object dereference.
  1839. //
  1840. LOCK_PFN (OldIrql);
  1841. ASSERT (Pfn1->u3.e2.ReferenceCount == 1);
  1842. ASSERT (Pfn2->u3.e2.ReferenceCount == 1);
  1843. #if (_MI_PAGING_LEVELS >= 3)
  1844. PageDirectoryFrame = MI_PFN_ELEMENT(Pfn1->u4.PteFrame)->u4.PteFrame;
  1845. PageParentFrame = MI_PFN_ELEMENT(PageDirectoryFrame)->u4.PteFrame;
  1846. ASSERT (PageDirectoryFrame == MI_PFN_ELEMENT(Pfn2->u4.PteFrame)->u4.PteFrame);
  1847. #endif
  1848. MiDecrementShareAndValidCount (Pfn1->u4.PteFrame);
  1849. MiDecrementShareAndValidCount (Pfn2->u4.PteFrame);
  1850. //
  1851. // N.B. Pfn1/2 and PageFrameIndex/2 cannot be referenced from here on out
  1852. // as the interlocked decrement has been done and another process
  1853. // may be racing through MmDeleteProcessAddressSpace.
  1854. //
  1855. MmResidentAvailablePages += CountReleased;
  1856. MM_BUMP_COUNTER(53, CountReleased);
  1857. MmResidentAvailablePages += MI_SESSION_SPACE_WORKING_SET_MINIMUM;
  1858. MM_BUMP_COUNTER(56, MI_SESSION_SPACE_WORKING_SET_MINIMUM);
  1859. #if (_MI_PAGING_LEVELS >= 3)
  1860. //
  1861. // Delete the session page directory and top level pages.
  1862. //
  1863. Pfn1 = MI_PFN_ELEMENT (PageDirectoryFrame);
  1864. MI_SET_PFN_DELETED (Pfn1);
  1865. MiDecrementShareCountOnly (PageDirectoryFrame);
  1866. MiDecrementShareCountOnly (PageDirectoryFrame);
  1867. Pfn1 = MI_PFN_ELEMENT (PageParentFrame);
  1868. MI_SET_PFN_DELETED (Pfn1);
  1869. MiDecrementShareCountOnly (PageParentFrame);
  1870. MiDecrementShareCountOnly (PageParentFrame);
  1871. #endif
  1872. //
  1873. // At this point everything has been deleted except the data pages.
  1874. //
  1875. // Delete page table pages. Note that the page table page mapping the
  1876. // session space data structure is done last so that we can apply
  1877. // various ASSERTs in the DeletePde routine.
  1878. //
  1879. SessionDataPdeIndex = MiGetPdeSessionIndex (MmSessionSpace);
  1880. for (Index = 0; Index < MiSessionSpacePageTables; Index += 1) {
  1881. if (Index == SessionDataPdeIndex) {
  1882. //
  1883. // The self map entry must be done last.
  1884. //
  1885. continue;
  1886. }
  1887. #if (_MI_PAGING_LEVELS >= 3)
  1888. MiSessionDeletePde (&SavePageTables[Index],
  1889. WorkingSetWasInitialized,
  1890. &SavePageTables[SessionDataPdeIndex]);
  1891. #else
  1892. MiSessionDeletePde (&SessionGlobal->PageTables[Index],
  1893. WorkingSetWasInitialized,
  1894. &SessionGlobal->PageTables[SessionDataPdeIndex]);
  1895. #endif
  1896. }
  1897. #if (_MI_PAGING_LEVELS >= 3)
  1898. MiSessionDeletePde (&SavePageTables[SessionDataPdeIndex],
  1899. WorkingSetWasInitialized,
  1900. &SavePageTables[SessionDataPdeIndex]);
  1901. #else
  1902. MiSessionDeletePde (&SessionGlobal->PageTables[SessionDataPdeIndex],
  1903. WorkingSetWasInitialized,
  1904. &SessionGlobal->PageTables[SessionDataPdeIndex]);
  1905. #endif
  1906. UNLOCK_PFN (OldIrql);
  1907. //
  1908. // Flush the session space TB entries.
  1909. //
  1910. MI_FLUSH_SESSION_TB ();
  1911. PS_CLEAR_BITS (&Process->Flags, PS_PROCESS_FLAGS_IN_SESSION);
  1912. //
  1913. // The session space has been deleted and all TB flushing is complete.
  1914. //
  1915. MmUnlockPagableImageSection (ExPageLockHandle);
  1916. return;
  1917. }
  1918. NTSTATUS
  1919. MiSessionCommitImagePages (
  1920. IN PVOID VirtualAddress,
  1921. IN SIZE_T NumberOfBytes
  1922. )
  1923. /*++
  1924. Routine Description:
  1925. This routine commits virtual memory within the current session space with
  1926. backing pages. The virtual addresses within session space are
  1927. allocated with a separate facility in the image management facility.
  1928. This is because images must be at a unique systemwide virtual address.
  1929. Arguments:
  1930. VirtualAddress - Supplies the first virtual address to commit.
  1931. NumberOfBytes - Supplies the number of bytes to commit.
  1932. Return Value:
  1933. STATUS_SUCCESS if all went well, STATUS_NO_MEMORY if the current process
  1934. has no session.
  1935. Environment:
  1936. Kernel mode, MmSystemLoadLock held.
  1937. This routine could be made PAGELK but it is a high frequency routine
  1938. so it is actually better to keep it nonpaged to avoid bringing in the
  1939. entire PAGELK section.
  1940. --*/
  1941. {
  1942. KIRQL WsIrql;
  1943. KIRQL OldIrql;
  1944. ULONG Color;
  1945. PFN_NUMBER SizeInPages;
  1946. PMMPFN Pfn1;
  1947. ULONG_PTR AllocationStart;
  1948. PFN_NUMBER PageFrameIndex;
  1949. NTSTATUS Status;
  1950. PMMPTE StartPte, EndPte;
  1951. MMPTE TempPte;
  1952. SYSLOAD_LOCK_OWNED_BY_ME ();
  1953. if (NumberOfBytes == 0) {
  1954. return STATUS_SUCCESS;
  1955. }
  1956. if (MmIsAddressValid(MmSessionSpace) == FALSE) {
  1957. #if DBG
  1958. DbgPrint ("MiSessionCommitImagePages: No session space!\n");
  1959. #endif
  1960. return STATUS_NO_MEMORY;
  1961. }
  1962. ASSERT (((ULONG_PTR)VirtualAddress % PAGE_SIZE) == 0);
  1963. ASSERT ((NumberOfBytes % PAGE_SIZE) == 0);
  1964. SizeInPages = (PFN_NUMBER)(NumberOfBytes >> PAGE_SHIFT);
  1965. if (MiChargeCommitment (SizeInPages, NULL) == FALSE) {
  1966. MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_NO_COMMIT);
  1967. return STATUS_NO_MEMORY;
  1968. }
  1969. //
  1970. // Calculate pages needed.
  1971. //
  1972. AllocationStart = (ULONG_PTR)VirtualAddress;
  1973. StartPte = MiGetPteAddress ((PVOID)AllocationStart);
  1974. EndPte = MiGetPteAddress ((PVOID)(AllocationStart + NumberOfBytes));
  1975. TempPte = ValidKernelPteLocal;
  1976. TempPte.u.Long |= MM_PTE_EXECUTE;
  1977. //
  1978. // Lock the session space working set.
  1979. //
  1980. LOCK_SESSION_SPACE_WS(WsIrql, PsGetCurrentThread ());
  1981. //
  1982. // Make sure we have page tables for the PTE
  1983. // entries we must fill in the session space structure.
  1984. //
  1985. Status = MiSessionCommitPageTables ((PVOID)AllocationStart,
  1986. (PVOID)(AllocationStart + NumberOfBytes));
  1987. if (!NT_SUCCESS(Status)) {
  1988. UNLOCK_SESSION_SPACE_WS(WsIrql);
  1989. MiReturnCommitment (SizeInPages);
  1990. return STATUS_NO_MEMORY;
  1991. }
  1992. //
  1993. // Loop allocating them and placing them into the page tables.
  1994. //
  1995. LOCK_PFN (OldIrql);
  1996. //
  1997. // Check to make sure the physical pages are available.
  1998. //
  1999. if ((SPFN_NUMBER)SizeInPages > MI_NONPAGABLE_MEMORY_AVAILABLE() - 20) {
  2000. UNLOCK_PFN (OldIrql);
  2001. MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_NO_RESIDENT);
  2002. UNLOCK_SESSION_SPACE_WS(WsIrql);
  2003. MiReturnCommitment (SizeInPages);
  2004. return STATUS_NO_MEMORY;
  2005. }
  2006. //
  2007. // Check to make sure the actual pages are currently available. Normally
  2008. // it would be fine to skip this check and use MiEnsureAvailablePageOrWait
  2009. // calls in the loop below which could release the session space mutex
  2010. // and wait - but this code has not been modified to handle rechecking
  2011. // all the state, so it's easier to just check once up front.
  2012. // The hardcoded 50 better always be higher than MM_HIGH_LIMIT.
  2013. //
  2014. if ((PFN_COUNT)SizeInPages + MM_HIGH_LIMIT + 50 > MmAvailablePages) {
  2015. UNLOCK_PFN (OldIrql);
  2016. MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_NO_AVAILABLE);
  2017. UNLOCK_SESSION_SPACE_WS(WsIrql);
  2018. MiReturnCommitment (SizeInPages);
  2019. return STATUS_NO_MEMORY;
  2020. }
  2021. MM_TRACK_COMMIT (MM_DBG_COMMIT_SESSION_IMAGE_PAGES, SizeInPages);
  2022. MmResidentAvailablePages -= SizeInPages;
  2023. MM_BUMP_COUNTER(45, SizeInPages);
  2024. while (StartPte < EndPte) {
  2025. ASSERT (StartPte->u.Long == ZeroKernelPte.u.Long);
  2026. #if 0
  2027. //
  2028. // If MiEnsureAvailablePageOrWait released the session space mutex
  2029. // while waiting for a page all this code would need to be modified
  2030. // to handle rechecking all the state. Instead the check was made
  2031. // once up front for enough currently available pages and the code
  2032. // here removed.
  2033. //
  2034. Waited = MiEnsureAvailablePageOrWait (HYDRA_PROCESS, NULL);
  2035. ASSERT (Waited == FALSE);
  2036. #endif
  2037. Color = MI_GET_PAGE_COLOR_FROM_SESSION (MmSessionSpace);
  2038. PageFrameIndex = MiRemoveZeroPage (Color);
  2039. TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
  2040. MI_WRITE_VALID_PTE (StartPte, TempPte);
  2041. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  2042. ASSERT (Pfn1->u1.WsIndex == 0);
  2043. MiInitializePfn (PageFrameIndex, StartPte, 1);
  2044. KeFillEntryTb ((PHARDWARE_PTE) StartPte, (PMMPTE)AllocationStart, FALSE);
  2045. StartPte += 1;
  2046. AllocationStart += PAGE_SIZE;
  2047. }
  2048. UNLOCK_PFN (OldIrql);
  2049. MmSessionSpace->NonPagablePages += SizeInPages;
  2050. MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_DRIVER_PAGES_LOCKED, (ULONG)SizeInPages);
  2051. UNLOCK_SESSION_SPACE_WS(WsIrql);
  2052. InterlockedExchangeAddSizeT (&MmSessionSpace->CommittedPages, SizeInPages);
  2053. MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_NP_COMMIT_IMAGE, (ULONG)SizeInPages);
  2054. return STATUS_SUCCESS;
  2055. }
  2056. NTSTATUS
  2057. MiSessionCommitPageTables (
  2058. IN PVOID StartVa,
  2059. IN PVOID EndVa
  2060. )
  2061. /*++
  2062. Routine Description:
  2063. Fill in page tables covering the specified virtual address range.
  2064. Arguments:
  2065. StartVa - Supplies a starting virtual address.
  2066. EndVa - Supplies an ending virtual address.
  2067. Return Value:
  2068. STATUS_SUCCESS on success, STATUS_NO_MEMORY on failure.
  2069. Environment:
  2070. Kernel mode. Session space working set mutex held.
  2071. This routine could be made PAGELK but it is a high frequency routine
  2072. so it is actually better to keep it nonpaged to avoid bringing in the
  2073. entire PAGELK section.
  2074. --*/
  2075. {
  2076. KIRQL OldIrql;
  2077. ULONG Color;
  2078. ULONG Index;
  2079. PMMPTE StartPde;
  2080. PMMPTE EndPde;
  2081. MMPTE TempPte;
  2082. PMMPFN Pfn1;
  2083. WSLE_NUMBER Entry;
  2084. WSLE_NUMBER SwapEntry;
  2085. PFN_NUMBER SizeInPages;
  2086. PFN_NUMBER PageTablePage;
  2087. PVOID SessionPte;
  2088. PMMWSL WorkingSetList;
  2089. ULONG AllocatedPageTables[(MI_SESSION_SPACE_MAXIMUM_PAGE_TABLES + sizeof(ULONG)*8-1)/(sizeof(ULONG)*8)];
  2090. ASSERT (MmIsAddressValid(MmSessionSpace) == TRUE);
  2091. MM_SESSION_SPACE_WS_LOCK_ASSERT();
  2092. ASSERT (StartVa >= (PVOID)MmSessionBase);
  2093. ASSERT (EndVa < (PVOID)MiSessionSpaceEnd);
  2094. //
  2095. // Allocate the page table pages, loading them
  2096. // into the current process's page directory.
  2097. //
  2098. StartPde = MiGetPdeAddress (StartVa);
  2099. EndPde = MiGetPdeAddress (EndVa);
  2100. Index = MiGetPdeSessionIndex (StartVa);
  2101. SizeInPages = 0;
  2102. while (StartPde <= EndPde) {
  2103. #if (_MI_PAGING_LEVELS >= 3)
  2104. if (StartPde->u.Long == ZeroKernelPte.u.Long)
  2105. #else
  2106. if (MmSessionSpace->PageTables[Index].u.Long == ZeroKernelPte.u.Long)
  2107. #endif
  2108. {
  2109. SizeInPages += 1;
  2110. }
  2111. StartPde += 1;
  2112. Index += 1;
  2113. }
  2114. if (SizeInPages == 0) {
  2115. return STATUS_SUCCESS;
  2116. }
  2117. if (MiChargeCommitment (SizeInPages, NULL) == FALSE) {
  2118. MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_NO_COMMIT);
  2119. return STATUS_NO_MEMORY;
  2120. }
  2121. RtlZeroMemory (AllocatedPageTables, sizeof (AllocatedPageTables));
  2122. WorkingSetList = MmSessionSpace->Vm.VmWorkingSetList;
  2123. StartPde = MiGetPdeAddress (StartVa);
  2124. Index = MiGetPdeSessionIndex (StartVa);
  2125. TempPte = ValidKernelPdeLocal;
  2126. LOCK_PFN (OldIrql);
  2127. //
  2128. // Check to make sure the physical pages are available.
  2129. //
  2130. if ((SPFN_NUMBER)SizeInPages > MI_NONPAGABLE_MEMORY_AVAILABLE() - 20) {
  2131. UNLOCK_PFN (OldIrql);
  2132. MiReturnCommitment (SizeInPages);
  2133. MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_NO_RESIDENT);
  2134. return STATUS_NO_MEMORY;
  2135. }
  2136. //
  2137. // Check to make sure the actual pages are currently available. Normally
  2138. // it would be fine to skip this check and use MiEnsureAvailablePageOrWait
  2139. // calls in the loop below which could release the session space mutex
  2140. // and wait - but this code has not been modified to handle rechecking
  2141. // all the state, so it's easier to just check once up front.
  2142. // The hardcoded 50 better always be higher than MM_HIGH_LIMIT.
  2143. //
  2144. if ((PFN_COUNT)SizeInPages + 50 > MmAvailablePages) {
  2145. UNLOCK_PFN (OldIrql);
  2146. MiReturnCommitment (SizeInPages);
  2147. MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_NO_AVAILABLE);
  2148. return STATUS_NO_MEMORY;
  2149. }
  2150. MM_TRACK_COMMIT (MM_DBG_COMMIT_SESSION_PAGETABLE_PAGES, SizeInPages);
  2151. MmResidentAvailablePages -= SizeInPages;
  2152. MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_PAGETABLE_ALLOC, (ULONG)SizeInPages);
  2153. MM_BUMP_COUNTER(44, SizeInPages);
  2154. while (StartPde <= EndPde) {
  2155. #if (_MI_PAGING_LEVELS >= 3)
  2156. if (StartPde->u.Long == ZeroKernelPte.u.Long)
  2157. #else
  2158. if (MmSessionSpace->PageTables[Index].u.Long == ZeroKernelPte.u.Long)
  2159. #endif
  2160. {
  2161. ASSERT (StartPde->u.Hard.Valid == 0);
  2162. MI_SET_BIT (AllocatedPageTables, Index);
  2163. #if 0
  2164. //
  2165. // If MiEnsureAvailablePageOrWait released the session space mutex
  2166. // while waiting for a page all this code would need to be modified
  2167. // to handle rechecking all the state. Instead the check was made
  2168. // once up front for enough currently available pages and the code
  2169. // here removed.
  2170. //
  2171. Waited = MiEnsureAvailablePageOrWait (HYDRA_PROCESS, NULL);
  2172. ASSERT (Waited == FALSE);
  2173. #endif
  2174. Color = MI_GET_PAGE_COLOR_FROM_SESSION (MmSessionSpace);
  2175. PageTablePage = MiRemoveZeroPage (Color);
  2176. TempPte.u.Hard.PageFrameNumber = PageTablePage;
  2177. MI_WRITE_VALID_PTE (StartPde, TempPte);
  2178. #if (_MI_PAGING_LEVELS < 3)
  2179. MmSessionSpace->PageTables[Index] = TempPte;
  2180. #endif
  2181. MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_NP_COMMIT_IMAGE_PT, 1);
  2182. MmSessionSpace->NonPagablePages += 1;
  2183. InterlockedExchangeAddSizeT (&MmSessionSpace->CommittedPages, 1);
  2184. MiInitializePfnForOtherProcess (PageTablePage,
  2185. StartPde,
  2186. MmSessionSpace->SessionPageDirectoryIndex);
  2187. }
  2188. StartPde += 1;
  2189. Index += 1;
  2190. }
  2191. UNLOCK_PFN (OldIrql);
  2192. StartPde = MiGetPdeAddress (StartVa);
  2193. Index = MiGetPdeSessionIndex (StartVa);
  2194. while (StartPde <= EndPde) {
  2195. if (MI_CHECK_BIT(AllocatedPageTables, Index)) {
  2196. ASSERT (StartPde->u.Hard.Valid == 1);
  2197. PageTablePage = MI_GET_PAGE_FRAME_FROM_PTE (StartPde);
  2198. Pfn1 = MI_PFN_ELEMENT (PageTablePage);
  2199. ASSERT (Pfn1->u1.Event == NULL);
  2200. Pfn1->u1.Event = (PVOID)PsGetCurrentThread ();
  2201. SessionPte = MiGetVirtualAddressMappedByPte (StartPde);
  2202. MiAddValidPageToWorkingSet (SessionPte,
  2203. StartPde,
  2204. Pfn1,
  2205. 0);
  2206. Entry = MiLocateWsle (SessionPte,
  2207. MmSessionSpace->Vm.VmWorkingSetList,
  2208. Pfn1->u1.WsIndex);
  2209. if (Entry >= WorkingSetList->FirstDynamic) {
  2210. SwapEntry = WorkingSetList->FirstDynamic;
  2211. if (Entry != WorkingSetList->FirstDynamic) {
  2212. //
  2213. // Swap this entry with the one at first dynamic.
  2214. //
  2215. MiSwapWslEntries (Entry, SwapEntry, &MmSessionSpace->Vm);
  2216. }
  2217. WorkingSetList->FirstDynamic += 1;
  2218. }
  2219. else {
  2220. SwapEntry = Entry;
  2221. }
  2222. //
  2223. // Indicate that the page is locked.
  2224. //
  2225. MmSessionSpace->Wsle[SwapEntry].u1.e1.LockedInWs = 1;
  2226. }
  2227. StartPde += 1;
  2228. Index += 1;
  2229. }
  2230. return STATUS_SUCCESS;
  2231. }
  2232. #if DBG
  2233. typedef struct _MISWAP {
  2234. ULONG Flag;
  2235. ULONG OutSwapCount;
  2236. PEPROCESS Process;
  2237. PMM_SESSION_SPACE Session;
  2238. } MISWAP, *PMISWAP;
  2239. ULONG MiSessionInfo[4];
  2240. MISWAP MiSessionSwap[0x100];
  2241. ULONG MiSwapIndex;
  2242. #endif
  2243. VOID
  2244. MiSessionOutSwapProcess (
  2245. IN PEPROCESS Process
  2246. )
  2247. /*++
  2248. Routine Description:
  2249. This routine notifies the containing session that the specified process is
  2250. being outswapped. When all the processes within a session have been
  2251. outswapped, the containing session undergoes a heavy trim.
  2252. Arguments:
  2253. Process - Supplies a pointer to the process that is swapped out of memory.
  2254. Return Value:
  2255. None.
  2256. Environment:
  2257. Kernel mode. This routine must not enter a wait state for memory resources
  2258. or the system will deadlock.
  2259. --*/
  2260. {
  2261. KIRQL OldIrql;
  2262. PMM_SESSION_SPACE SessionGlobal;
  2263. #if DBG
  2264. ULONG InCount;
  2265. ULONG OutCount;
  2266. PLIST_ENTRY NextEntry;
  2267. #endif
  2268. ASSERT (Process->Flags & PS_PROCESS_FLAGS_IN_SESSION);
  2269. //
  2270. // smss doesn't count when we swap it before it has detached from the
  2271. // session it is currently creating.
  2272. //
  2273. if (Process->Vm.Flags.SessionLeader == 1) {
  2274. return;
  2275. }
  2276. SessionGlobal = (PMM_SESSION_SPACE) Process->Session;
  2277. ASSERT (SessionGlobal != NULL);
  2278. LOCK_EXPANSION (OldIrql);
  2279. SessionGlobal->ProcessOutSwapCount += 1;
  2280. #if DBG
  2281. ASSERT ((LONG)SessionGlobal->ProcessOutSwapCount > 0);
  2282. InCount = 0;
  2283. OutCount = 0;
  2284. NextEntry = SessionGlobal->ProcessList.Flink;
  2285. while (NextEntry != &SessionGlobal->ProcessList) {
  2286. Process = CONTAINING_RECORD (NextEntry, EPROCESS, SessionProcessLinks);
  2287. if (Process->Flags & PS_PROCESS_FLAGS_OUTSWAP_ENABLED) {
  2288. OutCount += 1;
  2289. }
  2290. else {
  2291. InCount += 1;
  2292. }
  2293. NextEntry = NextEntry->Flink;
  2294. }
  2295. if (InCount + OutCount > SessionGlobal->ReferenceCount) {
  2296. DbgPrint ("MiSessionOutSwapProcess : process count mismatch %p %x %x %x\n",
  2297. SessionGlobal,
  2298. SessionGlobal->ReferenceCount,
  2299. InCount,
  2300. OutCount);
  2301. DbgBreakPoint ();
  2302. }
  2303. if (SessionGlobal->ProcessOutSwapCount != OutCount) {
  2304. DbgPrint ("MiSessionOutSwapProcess : out count mismatch %p %x %x %x %x\n",
  2305. SessionGlobal,
  2306. SessionGlobal->ReferenceCount,
  2307. SessionGlobal->ProcessOutSwapCount,
  2308. InCount,
  2309. OutCount);
  2310. DbgBreakPoint ();
  2311. }
  2312. ASSERT (SessionGlobal->ProcessOutSwapCount <= SessionGlobal->ReferenceCount);
  2313. MiSessionSwap[MiSwapIndex].Flag = 1;
  2314. MiSessionSwap[MiSwapIndex].Process = Process;
  2315. MiSessionSwap[MiSwapIndex].Session = SessionGlobal;
  2316. MiSessionSwap[MiSwapIndex].OutSwapCount = SessionGlobal->ProcessOutSwapCount;
  2317. MiSwapIndex += 1;
  2318. if (MiSwapIndex == 0x100) {
  2319. MiSwapIndex = 0;
  2320. }
  2321. #endif
  2322. if (SessionGlobal->ProcessOutSwapCount == SessionGlobal->ReferenceCount) {
  2323. SessionGlobal->Vm.Flags.TrimHard = 1;
  2324. #if DBG
  2325. if (MmDebug & MM_DBG_SESSIONS) {
  2326. DbgPrint ("Mm: Last process (%d total) just swapped out for session %d, %d pages\n",
  2327. SessionGlobal->ProcessOutSwapCount,
  2328. SessionGlobal->SessionId,
  2329. SessionGlobal->Vm.WorkingSetSize);
  2330. }
  2331. MiSessionInfo[0] += 1;
  2332. #endif
  2333. KeQuerySystemTime (&SessionGlobal->LastProcessSwappedOutTime);
  2334. }
  2335. #if DBG
  2336. else {
  2337. MiSessionInfo[1] += 1;
  2338. }
  2339. #endif
  2340. UNLOCK_EXPANSION (OldIrql);
  2341. }
  2342. VOID
  2343. MiSessionInSwapProcess (
  2344. IN PEPROCESS Process
  2345. )
  2346. /*++
  2347. Routine Description:
  2348. This routine in swaps the specified process.
  2349. Arguments:
  2350. Process - Supplies a pointer to the process that is to be swapped
  2351. into memory.
  2352. Return Value:
  2353. None.
  2354. Environment:
  2355. Kernel mode. This routine must not enter a wait state for memory resources
  2356. or the system will deadlock.
  2357. --*/
  2358. {
  2359. KIRQL OldIrql;
  2360. PMM_SESSION_SPACE SessionGlobal;
  2361. #if DBG
  2362. ULONG InCount;
  2363. ULONG OutCount;
  2364. PLIST_ENTRY NextEntry;
  2365. #endif
  2366. ASSERT (Process->Flags & PS_PROCESS_FLAGS_IN_SESSION);
  2367. //
  2368. // smss doesn't count when we swap it before it has detached from the
  2369. // session it is currently creating.
  2370. //
  2371. if (Process->Vm.Flags.SessionLeader == 1) {
  2372. return;
  2373. }
  2374. SessionGlobal = (PMM_SESSION_SPACE) Process->Session;
  2375. ASSERT (SessionGlobal != NULL);
  2376. LOCK_EXPANSION (OldIrql);
  2377. #if DBG
  2378. ASSERT ((LONG)SessionGlobal->ProcessOutSwapCount > 0);
  2379. InCount = 0;
  2380. OutCount = 0;
  2381. NextEntry = SessionGlobal->ProcessList.Flink;
  2382. while (NextEntry != &SessionGlobal->ProcessList) {
  2383. Process = CONTAINING_RECORD (NextEntry, EPROCESS, SessionProcessLinks);
  2384. if (Process->Flags & PS_PROCESS_FLAGS_OUTSWAP_ENABLED) {
  2385. OutCount += 1;
  2386. }
  2387. else {
  2388. InCount += 1;
  2389. }
  2390. NextEntry = NextEntry->Flink;
  2391. }
  2392. if (InCount + OutCount > SessionGlobal->ReferenceCount) {
  2393. DbgPrint ("MiSessionInSwapProcess : count mismatch %p %x %x %x\n",
  2394. SessionGlobal,
  2395. SessionGlobal->ReferenceCount,
  2396. InCount,
  2397. OutCount);
  2398. DbgBreakPoint ();
  2399. }
  2400. if (SessionGlobal->ProcessOutSwapCount != OutCount) {
  2401. DbgPrint ("MiSessionInSwapProcess : out count mismatch %p %x %x %x %x\n",
  2402. SessionGlobal,
  2403. SessionGlobal->ReferenceCount,
  2404. SessionGlobal->ProcessOutSwapCount,
  2405. InCount,
  2406. OutCount);
  2407. DbgBreakPoint ();
  2408. }
  2409. ASSERT (SessionGlobal->ProcessOutSwapCount <= SessionGlobal->ReferenceCount);
  2410. MiSessionSwap[MiSwapIndex].Flag = 2;
  2411. MiSessionSwap[MiSwapIndex].Process = Process;
  2412. MiSessionSwap[MiSwapIndex].Session = SessionGlobal;
  2413. MiSessionSwap[MiSwapIndex].OutSwapCount = SessionGlobal->ProcessOutSwapCount;
  2414. MiSwapIndex += 1;
  2415. if (MiSwapIndex == 0x100) {
  2416. MiSwapIndex = 0;
  2417. }
  2418. #endif
  2419. if (SessionGlobal->ProcessOutSwapCount == SessionGlobal->ReferenceCount) {
  2420. #if DBG
  2421. MiSessionInfo[2] += 1;
  2422. if (MmDebug & MM_DBG_SESSIONS) {
  2423. DbgPrint ("Mm: First process (%d total) just swapped back in for session %d, %d pages\n",
  2424. SessionGlobal->ProcessOutSwapCount,
  2425. SessionGlobal->SessionId,
  2426. SessionGlobal->Vm.WorkingSetSize);
  2427. }
  2428. #endif
  2429. SessionGlobal->Vm.Flags.TrimHard = 0;
  2430. }
  2431. #if DBG
  2432. else {
  2433. MiSessionInfo[3] += 1;
  2434. }
  2435. #endif
  2436. SessionGlobal->ProcessOutSwapCount -= 1;
  2437. ASSERT ((LONG)SessionGlobal->ProcessOutSwapCount >= 0);
  2438. UNLOCK_EXPANSION (OldIrql);
  2439. }
  2440. ULONG
  2441. MmGetSessionId (
  2442. IN PEPROCESS Process
  2443. )
  2444. /*++
  2445. Routine Description:
  2446. This routine returns the session ID of the specified process.
  2447. Arguments:
  2448. Process - Supplies a pointer to the process whose session ID is desired.
  2449. Return Value:
  2450. The session ID. Note these are recycled when sessions exit, hence the
  2451. caller must use proper object referencing on the specified process.
  2452. Environment:
  2453. Kernel mode. PASSIVE_LEVEL.
  2454. --*/
  2455. {
  2456. PMM_SESSION_SPACE SessionGlobal;
  2457. if (Process->Vm.Flags.SessionLeader == 1) {
  2458. //
  2459. // smss may transiently have a session space but that's of no interest
  2460. // to our caller.
  2461. //
  2462. return 0;
  2463. }
  2464. //
  2465. // The Session field of the EPROCESS is never cleared once set so these
  2466. // checks can be done lock free.
  2467. //
  2468. SessionGlobal = (PMM_SESSION_SPACE) Process->Session;
  2469. if (SessionGlobal == NULL) {
  2470. //
  2471. // The system process has no session space.
  2472. //
  2473. return 0;
  2474. }
  2475. SessionGlobal = (PMM_SESSION_SPACE) Process->Session;
  2476. return SessionGlobal->SessionId;
  2477. }
  2478. PVOID
  2479. MmGetNextSession (
  2480. IN PVOID OpaqueSession
  2481. )
  2482. /*++
  2483. Routine Description:
  2484. This function allows code to enumerate all the sessions in the system.
  2485. The first session (if OpaqueSession is NULL) or subsequent session
  2486. (if session is not NULL) is returned on each call.
  2487. If OpaqueSession is not NULL then this session must have previously
  2488. been obtained by a call to MmGetNextSession.
  2489. Enumeration may be terminated early by calling MmQuitNextSession on
  2490. the last non-NULL session returned by MmGetNextSession.
  2491. Sessions may be referenced in this manner and used later safely.
  2492. For example, to enumerate all sessions in a loop use this code fragment:
  2493. for (OpaqueSession = MmGetNextSession (NULL);
  2494. OpaqueSession != NULL;
  2495. OpaqueSession = MmGetNextSession (OpaqueSession)) {
  2496. ...
  2497. ...
  2498. //
  2499. // Checking for a specific session (if needed) is handled like this:
  2500. //
  2501. if (MmGetSessionId (OpaqueSession) == DesiredId) {
  2502. //
  2503. // Attach to session now to perform operations...
  2504. //
  2505. KAPC_STATE ApcState;
  2506. if (NT_SUCCESS (MmAttachSession (OpaqueSession, &ApcState))) {
  2507. //
  2508. // Session hasn't exited yet, so call interesting work
  2509. // functions that need session context ...
  2510. //
  2511. ...
  2512. //
  2513. // Detach from session.
  2514. //
  2515. MmDetachSession (OpaqueSession, &ApcState);
  2516. }
  2517. //
  2518. // If the interesting work functions failed and error recovery
  2519. // (ie: walk back through all the sessions already operated on
  2520. // and try to undo the actions), then do this. Note you must add
  2521. // similar checks to the above if the operations were only done
  2522. // to specifically requested session IDs.
  2523. //
  2524. if (ErrorRecoveryNeeded) {
  2525. for (OpaqueSession = MmGetPreviousSession (OpaqueSession);
  2526. OpaqueSession != NULL;
  2527. OpaqueSession = MmGetPreviousSession (OpaqueSession)) {
  2528. //
  2529. // MmAttachSession/DetachSession as needed to obtain
  2530. // context, etc.
  2531. //
  2532. }
  2533. break;
  2534. }
  2535. //
  2536. // Bail if only this session was of interest.
  2537. //
  2538. MmQuitNextSession (OpaqueSession);
  2539. break;
  2540. }
  2541. //
  2542. // Early terminating conditions are handled like this:
  2543. //
  2544. if (NeedToBreakOutEarly) {
  2545. MmQuitNextSession (OpaqueSession);
  2546. break;
  2547. }
  2548. }
  2549. Arguments:
  2550. OpaqueSession - Supplies the session to get the next session from
  2551. or NULL for the first session.
  2552. Return Value:
  2553. Next session or NULL if no more sessions exist.
  2554. --*/
  2555. {
  2556. KIRQL OldIrql;
  2557. PLIST_ENTRY NextEntry;
  2558. PMM_SESSION_SPACE Session;
  2559. PMM_SESSION_SPACE EntrySession;
  2560. PLIST_ENTRY NextProcessEntry;
  2561. PEPROCESS Process;
  2562. PVOID OpaqueNextSession;
  2563. PEPROCESS EntryProcess;
  2564. ASSERT (KeGetCurrentIrql () <= APC_LEVEL);
  2565. OpaqueNextSession = NULL;
  2566. EntryProcess = (PEPROCESS) OpaqueSession;
  2567. if (EntryProcess == NULL) {
  2568. EntrySession = NULL;
  2569. }
  2570. else {
  2571. ASSERT (EntryProcess->Vm.Flags.SessionLeader == 0);
  2572. //
  2573. // The Session field of the EPROCESS is never cleared once set so this
  2574. // field can be used lock free.
  2575. //
  2576. EntrySession = (PMM_SESSION_SPACE) EntryProcess->Session;
  2577. ASSERT (EntrySession != NULL);
  2578. }
  2579. LOCK_EXPANSION (OldIrql);
  2580. if (EntrySession == NULL) {
  2581. NextEntry = MiSessionWsList.Flink;
  2582. }
  2583. else {
  2584. NextEntry = EntrySession->WsListEntry.Flink;
  2585. }
  2586. while (NextEntry != &MiSessionWsList) {
  2587. Session = CONTAINING_RECORD (NextEntry, MM_SESSION_SPACE, WsListEntry);
  2588. NextProcessEntry = Session->ProcessList.Flink;
  2589. if ((Session->u.Flags.DeletePending == 0) &&
  2590. (NextProcessEntry != &Session->ProcessList)) {
  2591. Process = CONTAINING_RECORD (NextProcessEntry,
  2592. EPROCESS,
  2593. SessionProcessLinks);
  2594. if (Process->Vm.Flags.SessionLeader == 1) {
  2595. //
  2596. // If session manager is still the first process (ie: smss
  2597. // hasn't detached yet), then don't bother delivering to this
  2598. // session this early in its lifetime. And since smss is
  2599. // serialized, it can't be creating another session yet so
  2600. // just bail now as we must be at the end of the list.
  2601. //
  2602. break;
  2603. }
  2604. //
  2605. // If the process has finished rudimentary initialization, then
  2606. // select it as an attach can be performed safely. If this first
  2607. // process has not finished initializing there can be no others
  2608. // in this session, so just march on to the next session.
  2609. //
  2610. // Note the VmWorkingSetList is used instead of the
  2611. // AddressSpaceInitialized field because the VmWorkingSetList is
  2612. // never cleared so we can never see an exiting process (whose
  2613. // AddressSpaceInitialized field gets zeroed) and incorrectly
  2614. // decide the list must be empty.
  2615. //
  2616. if (Process->Vm.VmWorkingSetList != NULL) {
  2617. //
  2618. // Reference any process in the session so that the session
  2619. // cannot be completely deleted once the expansion lock is
  2620. // released (note this does NOT prevent the session from being
  2621. // cleaned).
  2622. //
  2623. ObReferenceObject (Process);
  2624. OpaqueNextSession = (PVOID) Process;
  2625. break;
  2626. }
  2627. }
  2628. NextEntry = NextEntry->Flink;
  2629. }
  2630. UNLOCK_EXPANSION (OldIrql);
  2631. //
  2632. // Regardless of whether a next session is returned, if a starting one
  2633. // was passed in, it must be dereferenced now.
  2634. //
  2635. if (EntryProcess != NULL) {
  2636. ObDereferenceObject (EntryProcess);
  2637. }
  2638. return OpaqueNextSession;
  2639. }
  2640. PVOID
  2641. MmGetPreviousSession (
  2642. IN PVOID OpaqueSession
  2643. )
  2644. /*++
  2645. Routine Description:
  2646. This function allows code to reverse-enumerate all the sessions in
  2647. the system. This is typically used for error recovery - ie: to walk
  2648. backwards undoing work done by MmGetNextSession semantics.
  2649. The first session (if OpaqueSession is NULL) or subsequent session
  2650. (if session is not NULL) is returned on each call.
  2651. If OpaqueSession is not NULL then this session must have previously
  2652. been obtained by a call to MmGetNextSession.
  2653. Arguments:
  2654. OpaqueSession - Supplies the session to get the next session from
  2655. or NULL for the first session.
  2656. Return Value:
  2657. Next session or NULL if no more sessions exist.
  2658. --*/
  2659. {
  2660. KIRQL OldIrql;
  2661. PLIST_ENTRY NextEntry;
  2662. PMM_SESSION_SPACE Session;
  2663. PMM_SESSION_SPACE EntrySession;
  2664. PLIST_ENTRY NextProcessEntry;
  2665. PEPROCESS Process;
  2666. PVOID OpaqueNextSession;
  2667. PEPROCESS EntryProcess;
  2668. ASSERT (KeGetCurrentIrql () <= APC_LEVEL);
  2669. OpaqueNextSession = NULL;
  2670. EntryProcess = (PEPROCESS) OpaqueSession;
  2671. if (EntryProcess == NULL) {
  2672. EntrySession = NULL;
  2673. }
  2674. else {
  2675. ASSERT (EntryProcess->Vm.Flags.SessionLeader == 0);
  2676. //
  2677. // The Session field of the EPROCESS is never cleared once set so this
  2678. // field can be used lock free.
  2679. //
  2680. EntrySession = (PMM_SESSION_SPACE) EntryProcess->Session;
  2681. ASSERT (EntrySession != NULL);
  2682. }
  2683. LOCK_EXPANSION (OldIrql);
  2684. if (EntrySession == NULL) {
  2685. NextEntry = MiSessionWsList.Blink;
  2686. }
  2687. else {
  2688. NextEntry = EntrySession->WsListEntry.Blink;
  2689. }
  2690. while (NextEntry != &MiSessionWsList) {
  2691. Session = CONTAINING_RECORD (NextEntry, MM_SESSION_SPACE, WsListEntry);
  2692. NextProcessEntry = Session->ProcessList.Flink;
  2693. if ((Session->u.Flags.DeletePending == 0) &&
  2694. (NextProcessEntry != &Session->ProcessList)) {
  2695. Process = CONTAINING_RECORD (NextProcessEntry,
  2696. EPROCESS,
  2697. SessionProcessLinks);
  2698. ASSERT (Process->Vm.Flags.SessionLeader == 0);
  2699. //
  2700. // Reference any process in the session so that the session
  2701. // cannot be completely deleted once the expansion lock is
  2702. // released (note this does NOT prevent the session from being
  2703. // cleaned).
  2704. //
  2705. ObReferenceObject (Process);
  2706. OpaqueNextSession = (PVOID) Process;
  2707. break;
  2708. }
  2709. NextEntry = NextEntry->Blink;
  2710. }
  2711. UNLOCK_EXPANSION (OldIrql);
  2712. //
  2713. // Regardless of whether a next session is returned, if a starting one
  2714. // was passed in, it must be dereferenced now.
  2715. //
  2716. if (EntryProcess != NULL) {
  2717. ObDereferenceObject (EntryProcess);
  2718. }
  2719. return OpaqueNextSession;
  2720. }
  2721. NTSTATUS
  2722. MmQuitNextSession (
  2723. IN PVOID OpaqueSession
  2724. )
  2725. /*++
  2726. Routine Description:
  2727. This function is used to prematurely terminate a session enumeration
  2728. that began using MmGetNextSession.
  2729. Arguments:
  2730. OpaqueSession - Supplies a non-NULL session previously obtained by
  2731. a call to MmGetNextSession.
  2732. Return Value:
  2733. NTSTATUS.
  2734. --*/
  2735. {
  2736. PEPROCESS EntryProcess;
  2737. ASSERT (KeGetCurrentIrql () <= APC_LEVEL);
  2738. EntryProcess = (PEPROCESS) OpaqueSession;
  2739. ASSERT (EntryProcess->Vm.Flags.SessionLeader == 0);
  2740. //
  2741. // The Session field of the EPROCESS is never cleared once set so this
  2742. // field can be used lock free.
  2743. //
  2744. ASSERT (EntryProcess->Session != NULL);
  2745. ObDereferenceObject (EntryProcess);
  2746. return STATUS_SUCCESS;
  2747. }
  2748. NTSTATUS
  2749. MmAttachSession (
  2750. IN PVOID OpaqueSession,
  2751. OUT PRKAPC_STATE ApcState
  2752. )
  2753. /*++
  2754. Routine Description:
  2755. This function attaches the calling thread to a referenced session
  2756. previously obtained via MmGetNextSession.
  2757. Arguments:
  2758. OpaqueSession - Supplies a non-NULL session previously obtained by
  2759. a call to MmGetNextSession.
  2760. ApcState - Supplies APC state storage for the subsequent detach.
  2761. Return Value:
  2762. NTSTATUS. If successful then we are attached on return. The caller is
  2763. responsible for calling MmDetachSession when done.
  2764. --*/
  2765. {
  2766. KIRQL OldIrql;
  2767. PEPROCESS EntryProcess;
  2768. PMM_SESSION_SPACE EntrySession;
  2769. PEPROCESS CurrentProcess;
  2770. PMM_SESSION_SPACE CurrentSession;
  2771. ASSERT (KeGetCurrentIrql () <= APC_LEVEL);
  2772. EntryProcess = (PEPROCESS) OpaqueSession;
  2773. ASSERT (EntryProcess->Vm.Flags.SessionLeader == 0);
  2774. //
  2775. // The Session field of the EPROCESS is never cleared once set so this
  2776. // field can be used lock free.
  2777. //
  2778. EntrySession = (PMM_SESSION_SPACE) EntryProcess->Session;
  2779. ASSERT (EntrySession != NULL);
  2780. CurrentProcess = PsGetCurrentProcess ();
  2781. CurrentSession = (PMM_SESSION_SPACE) CurrentProcess->Session;
  2782. LOCK_EXPANSION (OldIrql);
  2783. if (EntrySession->u.Flags.DeletePending == 1) {
  2784. UNLOCK_EXPANSION (OldIrql);
  2785. return STATUS_PROCESS_IS_TERMINATING;
  2786. }
  2787. EntrySession->AttachCount += 1;
  2788. UNLOCK_EXPANSION (OldIrql);
  2789. if ((CurrentProcess->Vm.Flags.SessionLeader == 0) &&
  2790. (CurrentSession != NULL)) {
  2791. //
  2792. // smss may transiently have a session space but that's of
  2793. // no interest to our caller.
  2794. //
  2795. if (CurrentSession == EntrySession) {
  2796. ASSERT (CurrentSession->SessionId == EntrySession->SessionId);
  2797. //
  2798. // The current and target sessions match so an attach is not needed.
  2799. // Call KeStackAttach anyway (this has the overhead of an extra
  2800. // dispatcher lock acquire and release) so that callers can always
  2801. // use MmDetachSession to detach. This is a very infrequent path so
  2802. // the extra lock acquire and release is not significant.
  2803. //
  2804. // Note that by resetting EntryProcess below, an attach will not
  2805. // actually occur.
  2806. //
  2807. EntryProcess = CurrentProcess;
  2808. }
  2809. else {
  2810. ASSERT (CurrentSession->SessionId != EntrySession->SessionId);
  2811. }
  2812. }
  2813. KeStackAttachProcess (&EntryProcess->Pcb, ApcState);
  2814. return STATUS_SUCCESS;
  2815. }
  2816. NTSTATUS
  2817. MmDetachSession (
  2818. IN PVOID OpaqueSession,
  2819. IN PRKAPC_STATE ApcState
  2820. )
  2821. /*++
  2822. Routine Description:
  2823. This function detaches the calling thread from the referenced session
  2824. previously attached to via MmAttachSession.
  2825. Arguments:
  2826. OpaqueSession - Supplies a non-NULL session previously obtained by
  2827. a call to MmGetNextSession.
  2828. ApcState - Supplies APC state storage information for the detach.
  2829. Return Value:
  2830. NTSTATUS. If successful then we are detached on return. The caller is
  2831. responsible for eventually calling MmQuitNextSession on return.
  2832. --*/
  2833. {
  2834. KIRQL OldIrql;
  2835. PEPROCESS EntryProcess;
  2836. PMM_SESSION_SPACE EntrySession;
  2837. ASSERT (KeGetCurrentIrql () <= APC_LEVEL);
  2838. EntryProcess = (PEPROCESS) OpaqueSession;
  2839. ASSERT (EntryProcess->Vm.Flags.SessionLeader == 0);
  2840. //
  2841. // The Session field of the EPROCESS is never cleared once set so this
  2842. // field can be used lock free.
  2843. //
  2844. EntrySession = (PMM_SESSION_SPACE) EntryProcess->Session;
  2845. ASSERT (EntrySession != NULL);
  2846. LOCK_EXPANSION (OldIrql);
  2847. ASSERT (EntrySession->AttachCount >= 1);
  2848. EntrySession->AttachCount -= 1;
  2849. if ((EntrySession->u.Flags.DeletePending == 0) ||
  2850. (EntrySession->AttachCount != 0)) {
  2851. EntrySession = NULL;
  2852. }
  2853. UNLOCK_EXPANSION (OldIrql);
  2854. KeUnstackDetachProcess (ApcState);
  2855. if (EntrySession != NULL) {
  2856. KeSetEvent (&EntrySession->AttachEvent, 0, FALSE);
  2857. }
  2858. return STATUS_SUCCESS;
  2859. }
  2860. PVOID
  2861. MmGetSessionById (
  2862. IN ULONG SessionId
  2863. )
  2864. /*++
  2865. Routine Description:
  2866. This function allows callers to obtain a reference to a specific session.
  2867. The caller can then MmAttachSession, MmDetachSession & MmQuitNextSession
  2868. to complete the proper sequence so reference counting and address context
  2869. operate properly.
  2870. Arguments:
  2871. SessionId - Supplies the session ID of the desired session.
  2872. Return Value:
  2873. An opaque session token or NULL if the session cannot be found.
  2874. Environment:
  2875. Kernel mode, the caller must guarantee the session cannot exit or the ID
  2876. becomes meaningless as it can be reused.
  2877. --*/
  2878. {
  2879. KIRQL OldIrql;
  2880. PLIST_ENTRY NextEntry;
  2881. PMM_SESSION_SPACE Session;
  2882. PLIST_ENTRY NextProcessEntry;
  2883. PEPROCESS Process;
  2884. PVOID OpaqueSession;
  2885. ASSERT (KeGetCurrentIrql () <= APC_LEVEL);
  2886. OpaqueSession = NULL;
  2887. LOCK_EXPANSION (OldIrql);
  2888. NextEntry = MiSessionWsList.Flink;
  2889. while (NextEntry != &MiSessionWsList) {
  2890. Session = CONTAINING_RECORD (NextEntry, MM_SESSION_SPACE, WsListEntry);
  2891. NextProcessEntry = Session->ProcessList.Flink;
  2892. if (Session->SessionId == SessionId) {
  2893. if ((Session->u.Flags.DeletePending != 0) ||
  2894. (NextProcessEntry == &Session->ProcessList)) {
  2895. //
  2896. // Session is empty or exiting so return failure to the caller.
  2897. //
  2898. break;
  2899. }
  2900. Process = CONTAINING_RECORD (NextProcessEntry,
  2901. EPROCESS,
  2902. SessionProcessLinks);
  2903. if (Process->Vm.Flags.SessionLeader == 1) {
  2904. //
  2905. // Session manager is still the first process (ie: smss
  2906. // hasn't detached yet), don't bother delivering to this
  2907. // session this early in its lifetime. And since smss is
  2908. // serialized, it can't be creating another session yet so
  2909. // just bail now as we must be at the end of the list.
  2910. //
  2911. break;
  2912. }
  2913. //
  2914. // Reference any process in the session so that the session
  2915. // cannot be completely deleted once the expansion lock is
  2916. // released (note this does NOT prevent the session from being
  2917. // cleaned).
  2918. //
  2919. ObReferenceObject (Process);
  2920. OpaqueSession = (PVOID) Process;
  2921. break;
  2922. }
  2923. NextEntry = NextEntry->Flink;
  2924. }
  2925. UNLOCK_EXPANSION (OldIrql);
  2926. return OpaqueSession;
  2927. }
  2928. PVOID
  2929. MiAttachToSecureProcessInSession (
  2930. IN PRKAPC_STATE ApcState
  2931. )
  2932. /*++
  2933. Routine Description:
  2934. This function allows callers to attach to a secure process
  2935. in the current session. This is the first process created in it.
  2936. Arguments:
  2937. ApcState - Supplies APC state storage information for the attach.
  2938. Return Value:
  2939. An opaque session token or NULL if the attach could not be performed.
  2940. If non-null, then the process attach been performed on return.
  2941. Environment:
  2942. Kernel mode.
  2943. --*/
  2944. {
  2945. KIRQL OldIrql;
  2946. PLIST_ENTRY NextProcessEntry;
  2947. PEPROCESS TargetProcess;
  2948. PMM_SESSION_SPACE SessionGlobal;
  2949. SessionGlobal = SESSION_GLOBAL(MmSessionSpace);
  2950. LOCK_EXPANSION (OldIrql);
  2951. NextProcessEntry = SessionGlobal->ProcessList.Flink;
  2952. if ((SessionGlobal->u.Flags.DeletePending == 0) &&
  2953. (NextProcessEntry != &SessionGlobal->ProcessList)) {
  2954. TargetProcess = CONTAINING_RECORD (NextProcessEntry,
  2955. EPROCESS,
  2956. SessionProcessLinks);
  2957. ObReferenceObject (TargetProcess);
  2958. }
  2959. else {
  2960. TargetProcess = NULL;
  2961. }
  2962. UNLOCK_EXPANSION (OldIrql);
  2963. if (TargetProcess != NULL) {
  2964. KeStackAttachProcess (&TargetProcess->Pcb, ApcState);
  2965. }
  2966. return TargetProcess;
  2967. }
  2968. VOID
  2969. MiDetachFromSecureProcessInSession (
  2970. IN PVOID OpaqueSession,
  2971. IN PRKAPC_STATE ApcState
  2972. )
  2973. /*++
  2974. Routine Description:
  2975. This function allows callers to detach from the currently secure process
  2976. in the current session (and dereference the secure process object).
  2977. Arguments:
  2978. OpaqueSession - Supplies the opaque session value returned by
  2979. MiAttachToSecureProcessInSession.
  2980. ApcState - Supplies APC state storage information for the detach.
  2981. Return Value:
  2982. None.
  2983. Environment:
  2984. Kernel mode, APC_LEVEL or below.
  2985. --*/
  2986. {
  2987. PEPROCESS TargetProcess;
  2988. TargetProcess = (PEPROCESS) OpaqueSession;
  2989. KeUnstackDetachProcess (ApcState);
  2990. ObDereferenceObject (TargetProcess);
  2991. }