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

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