Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1156 lines
29 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. lockvm.c
  5. Abstract:
  6. This module contains the routines which implement the
  7. NtLockVirtualMemory service.
  8. Author:
  9. Lou Perazzoli (loup) 20-August-1989
  10. Landy Wang (landyw) 02-June-1997
  11. Revision History:
  12. --*/
  13. #include "mi.h"
  14. #ifdef ALLOC_PRAGMA
  15. #pragma alloc_text(PAGE,NtLockVirtualMemory)
  16. #pragma alloc_text(PAGE,NtUnlockVirtualMemory)
  17. #endif
  18. NTSTATUS
  19. NtLockVirtualMemory (
  20. IN HANDLE ProcessHandle,
  21. IN OUT PVOID *BaseAddress,
  22. IN OUT PSIZE_T RegionSize,
  23. IN ULONG MapType
  24. )
  25. /*++
  26. Routine Description:
  27. This function locks a region of pages within the working set list
  28. of a subject process.
  29. The caller of this function must have PROCESS_VM_OPERATION access
  30. to the target process. The caller must also have SeLockMemoryPrivilege.
  31. Arguments:
  32. ProcessHandle - Supplies an open handle to a process object.
  33. BaseAddress - The base address of the region of pages
  34. to be locked. This value is rounded down to the
  35. next host page address boundary.
  36. RegionSize - A pointer to a variable that will receive
  37. the actual size in bytes of the locked region of
  38. pages. The initial value of this argument is
  39. rounded up to the next host page size boundary.
  40. MapType - A set of flags that describe the type of locking to
  41. perform. One of MAP_PROCESS or MAP_SYSTEM.
  42. Return Value:
  43. NTSTATUS.
  44. STATUS_PRIVILEGE_NOT_HELD - The caller did not have sufficient
  45. privilege to perform the requested operation.
  46. --*/
  47. {
  48. PVOID Va;
  49. PVOID StartingVa;
  50. PVOID EndingAddress;
  51. KAPC_STATE ApcState;
  52. PMMPTE PointerPte;
  53. PMMPTE PointerPte1;
  54. PMMPFN Pfn1;
  55. PMMPTE PointerPde;
  56. PMMPTE PointerPpe;
  57. PMMPTE PointerPxe;
  58. ULONG_PTR CapturedRegionSize;
  59. PVOID CapturedBase;
  60. PEPROCESS TargetProcess;
  61. NTSTATUS Status;
  62. LOGICAL WasLocked;
  63. KPROCESSOR_MODE PreviousMode;
  64. WSLE_NUMBER Entry;
  65. WSLE_NUMBER SwapEntry;
  66. SIZE_T NumberOfAlreadyLocked;
  67. SIZE_T NumberToLock;
  68. WSLE_NUMBER WorkingSetIndex;
  69. PMMVAD Vad;
  70. PVOID LastVa;
  71. ULONG Waited;
  72. LOGICAL Attached;
  73. PETHREAD Thread;
  74. #if defined(_MIALT4K_)
  75. PVOID Wow64Process;
  76. #endif
  77. PAGED_CODE();
  78. WasLocked = FALSE;
  79. LastVa = NULL;
  80. //
  81. // Validate the flags in MapType.
  82. //
  83. if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)) != 0) {
  84. return STATUS_INVALID_PARAMETER;
  85. }
  86. if ((MapType & (MAP_PROCESS | MAP_SYSTEM)) == 0) {
  87. return STATUS_INVALID_PARAMETER;
  88. }
  89. Thread = PsGetCurrentThread ();
  90. PreviousMode = KeGetPreviousModeByThread(&Thread->Tcb);
  91. try {
  92. if (PreviousMode != KernelMode) {
  93. ProbeForWritePointer ((PULONG)BaseAddress);
  94. ProbeForWriteUlong_ptr (RegionSize);
  95. }
  96. //
  97. // Capture the base address.
  98. //
  99. CapturedBase = *BaseAddress;
  100. //
  101. // Capture the region size.
  102. //
  103. CapturedRegionSize = *RegionSize;
  104. } except (ExSystemExceptionFilter()) {
  105. //
  106. // If an exception occurs during the probe or capture
  107. // of the initial values, then handle the exception and
  108. // return the exception code as the status value.
  109. //
  110. return GetExceptionCode();
  111. }
  112. //
  113. // Make sure the specified starting and ending addresses are
  114. // within the user part of the virtual address space.
  115. //
  116. if (CapturedBase > MM_HIGHEST_USER_ADDRESS) {
  117. //
  118. // Invalid base address.
  119. //
  120. return STATUS_INVALID_PARAMETER;
  121. }
  122. if ((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (ULONG_PTR)CapturedBase <
  123. CapturedRegionSize) {
  124. //
  125. // Invalid region size;
  126. //
  127. return STATUS_INVALID_PARAMETER;
  128. }
  129. if (CapturedRegionSize == 0) {
  130. return STATUS_INVALID_PARAMETER;
  131. }
  132. //
  133. // Reference the specified process.
  134. //
  135. Status = ObReferenceObjectByHandle ( ProcessHandle,
  136. PROCESS_VM_OPERATION,
  137. PsProcessType,
  138. PreviousMode,
  139. (PVOID *)&TargetProcess,
  140. NULL );
  141. if (!NT_SUCCESS(Status)) {
  142. return Status;
  143. }
  144. if ((MapType & MAP_SYSTEM) != 0) {
  145. //
  146. // In addition to PROCESS_VM_OPERATION access to the target
  147. // process, the caller must have SE_LOCK_MEMORY_PRIVILEGE.
  148. //
  149. if (!SeSinglePrivilegeCheck(
  150. SeLockMemoryPrivilege,
  151. PreviousMode
  152. )) {
  153. ObDereferenceObject( TargetProcess );
  154. return( STATUS_PRIVILEGE_NOT_HELD );
  155. }
  156. }
  157. //
  158. // Attach to the specified process.
  159. //
  160. if (ProcessHandle != NtCurrentProcess()) {
  161. KeStackAttachProcess (&TargetProcess->Pcb, &ApcState);
  162. Attached = TRUE;
  163. }
  164. else {
  165. Attached = FALSE;
  166. }
  167. //
  168. // Get address creation mutex, this prevents the
  169. // address range from being modified while it is examined. Raise
  170. // to APC level to prevent an APC routine from acquiring the
  171. // address creation mutex. Get the working set mutex so the
  172. // number of already locked pages in the request can be determined.
  173. //
  174. #if defined(_MIALT4K_)
  175. //
  176. // Changing to 4k aligned should not change the correctness.
  177. //
  178. EndingAddress = PAGE_4K_ALIGN((PCHAR)CapturedBase + CapturedRegionSize - 1);
  179. #else
  180. EndingAddress = PAGE_ALIGN((PCHAR)CapturedBase + CapturedRegionSize - 1);
  181. #endif
  182. Va = PAGE_ALIGN (CapturedBase);
  183. NumberOfAlreadyLocked = 0;
  184. NumberToLock = ((ULONG_PTR)EndingAddress - (ULONG_PTR)Va) >> PAGE_SHIFT;
  185. LOCK_ADDRESS_SPACE (TargetProcess);
  186. //
  187. // Make sure the address space was not deleted, if so, return an error.
  188. //
  189. if (TargetProcess->Flags & PS_PROCESS_FLAGS_VM_DELETED) {
  190. Status = STATUS_PROCESS_IS_TERMINATING;
  191. goto ErrorReturn1;
  192. }
  193. if (NumberToLock + MM_FLUID_WORKING_SET >
  194. TargetProcess->Vm.MinimumWorkingSetSize) {
  195. Status = STATUS_WORKING_SET_QUOTA;
  196. goto ErrorReturn1;
  197. }
  198. //
  199. // Note the working set mutex must be held throughout the loop below to
  200. // prevent other threads from locking or unlocking WSL entries.
  201. //
  202. LOCK_WS_UNSAFE (TargetProcess);
  203. while (Va <= EndingAddress) {
  204. if (Va > LastVa) {
  205. //
  206. // Don't lock physically mapped views.
  207. //
  208. Vad = MiLocateAddress (Va);
  209. if (Vad == NULL) {
  210. Status = STATUS_ACCESS_VIOLATION;
  211. goto ErrorReturn;
  212. }
  213. if ((Vad->u.VadFlags.PhysicalMapping == 1) ||
  214. (Vad->u.VadFlags.UserPhysicalPages == 1)) {
  215. Status = STATUS_INCOMPATIBLE_FILE_MAP;
  216. goto ErrorReturn;
  217. }
  218. LastVa = MI_VPN_TO_VA (Vad->EndingVpn);
  219. }
  220. if (MmIsAddressValid (Va)) {
  221. //
  222. // The page is valid, therefore it is in the working set.
  223. // Locate the WSLE for the page and see if it is locked.
  224. //
  225. PointerPte1 = MiGetPteAddress (Va);
  226. Pfn1 = MI_PFN_ELEMENT (PointerPte1->u.Hard.PageFrameNumber);
  227. WorkingSetIndex = MiLocateWsle (Va,
  228. MmWorkingSetList,
  229. Pfn1->u1.WsIndex);
  230. ASSERT (WorkingSetIndex != WSLE_NULL_INDEX);
  231. if (WorkingSetIndex < MmWorkingSetList->FirstDynamic) {
  232. //
  233. // This page is locked in the working set.
  234. //
  235. NumberOfAlreadyLocked += 1;
  236. //
  237. // Check to see if the WAS_LOCKED status should be returned.
  238. //
  239. if ((MapType & MAP_PROCESS) &&
  240. (MmWsle[WorkingSetIndex].u1.e1.LockedInWs == 1)) {
  241. WasLocked = TRUE;
  242. }
  243. if ((MapType & MAP_SYSTEM) &&
  244. (MmWsle[WorkingSetIndex].u1.e1.LockedInMemory == 1)) {
  245. WasLocked = TRUE;
  246. }
  247. }
  248. }
  249. Va = (PVOID)((PCHAR)Va + PAGE_SIZE);
  250. }
  251. UNLOCK_WS_UNSAFE (TargetProcess);
  252. //
  253. // Check to ensure the working set list is still fluid after
  254. // the requested number of pages are locked.
  255. //
  256. if (TargetProcess->Vm.MinimumWorkingSetSize <
  257. ((MmWorkingSetList->FirstDynamic + NumberToLock +
  258. MM_FLUID_WORKING_SET) - NumberOfAlreadyLocked)) {
  259. Status = STATUS_WORKING_SET_QUOTA;
  260. goto ErrorReturn1;
  261. }
  262. Va = PAGE_ALIGN (CapturedBase);
  263. #if defined(_MIALT4K_)
  264. Wow64Process = TargetProcess->Wow64Process;
  265. if (Wow64Process != NULL) {
  266. Va = PAGE_4K_ALIGN (CapturedBase);
  267. }
  268. #endif
  269. //
  270. // Set up an exception handler and touch each page in the specified
  271. // range. Mark this thread as the address space mutex owner so it cannot
  272. // sneak its stack in as the argument region and trick us into trying to
  273. // grow it if the reference faults (as this would cause a deadlock since
  274. // this thread already owns the address space mutex). Note this would have
  275. // the side effect of not allowing this thread to fault on guard pages in
  276. // other data regions while the accesses below are ongoing - but that could
  277. // only happen in an APC and those are blocked right now anyway.
  278. //
  279. ASSERT (KeGetCurrentIrql () == APC_LEVEL);
  280. ASSERT (Thread->AddressSpaceOwner == 0);
  281. Thread->AddressSpaceOwner = 1;
  282. try {
  283. while (Va <= EndingAddress) {
  284. *(volatile ULONG *)Va;
  285. Va = (PVOID)((PCHAR)Va + PAGE_SIZE);
  286. }
  287. } except (EXCEPTION_EXECUTE_HANDLER) {
  288. Status = GetExceptionCode();
  289. ASSERT (KeGetCurrentIrql () == APC_LEVEL);
  290. ASSERT (Thread->AddressSpaceOwner == 1);
  291. Thread->AddressSpaceOwner = 0;
  292. goto ErrorReturn1;
  293. }
  294. ASSERT (KeGetCurrentIrql () == APC_LEVEL);
  295. ASSERT (Thread->AddressSpaceOwner == 1);
  296. Thread->AddressSpaceOwner = 0;
  297. //
  298. // The complete address range is accessible, lock the pages into
  299. // the working set.
  300. //
  301. PointerPte = MiGetPteAddress (CapturedBase);
  302. Va = PAGE_ALIGN (CapturedBase);
  303. #if defined(_MIALT4K_)
  304. if (Wow64Process != NULL) {
  305. Va = PAGE_4K_ALIGN (CapturedBase);
  306. }
  307. #endif
  308. StartingVa = Va;
  309. //
  310. // Acquire the working set mutex, no page faults are allowed.
  311. //
  312. LOCK_WS_UNSAFE (TargetProcess);
  313. while (Va <= EndingAddress) {
  314. //
  315. // Make sure the PDE is valid.
  316. //
  317. PointerPde = MiGetPdeAddress (Va);
  318. PointerPpe = MiGetPpeAddress (Va);
  319. PointerPxe = MiGetPxeAddress (Va);
  320. do {
  321. MiDoesPxeExistAndMakeValid (PointerPxe,
  322. TargetProcess,
  323. FALSE,
  324. &Waited);
  325. #if (_MI_PAGING_LEVELS >= 4)
  326. Waited = 0;
  327. #endif
  328. MiDoesPpeExistAndMakeValid (PointerPpe,
  329. TargetProcess,
  330. FALSE,
  331. &Waited);
  332. #if (_MI_PAGING_LEVELS < 4)
  333. Waited = 0;
  334. #endif
  335. MiDoesPdeExistAndMakeValid (PointerPde,
  336. TargetProcess,
  337. FALSE,
  338. &Waited);
  339. } while (Waited != 0);
  340. //
  341. // Make sure the page is in the working set.
  342. //
  343. while (PointerPte->u.Hard.Valid == 0) {
  344. //
  345. // Release the working set mutex and fault in the page.
  346. //
  347. UNLOCK_WS_UNSAFE (TargetProcess);
  348. //
  349. // Page in the PDE and make the PTE valid.
  350. //
  351. try {
  352. *(volatile ULONG *)Va;
  353. } except (EXCEPTION_EXECUTE_HANDLER) {
  354. //
  355. // Since all the pages were accessed above with the address
  356. // space mutex held and it is still held now, the only way
  357. // an exception could occur would be due to a device error,
  358. // ie: hardware malfunction, net cable disconnection, CD
  359. // being removed, etc.
  360. //
  361. // Recompute EndingAddress so the actual number of pages locked
  362. // is written back to the user now. If this is the very first
  363. // page then return a failure status.
  364. //
  365. EndingAddress = PAGE_ALIGN (Va);
  366. #if defined(_MIALT4K_)
  367. if (Wow64Process != NULL) {
  368. EndingAddress = PAGE_4K_ALIGN (Va);
  369. }
  370. #endif
  371. if (EndingAddress == StartingVa) {
  372. Status = GetExceptionCode ();
  373. goto ErrorReturn1;
  374. }
  375. ASSERT (NT_SUCCESS (Status));
  376. EndingAddress = (PVOID)((ULONG_PTR)EndingAddress - 1);
  377. #if defined(_MIALT4K_)
  378. if (Wow64Process != NULL) {
  379. CapturedRegionSize = (ULONG_PTR)EndingAddress - (ULONG_PTR)CapturedBase;
  380. }
  381. #endif
  382. goto SuccessReturn1;
  383. }
  384. //
  385. // Reacquire the working set mutex.
  386. //
  387. LOCK_WS_UNSAFE (TargetProcess);
  388. //
  389. // Make sure the page directory & table pages are still valid.
  390. // Trimming could occur if either of the pages that were just
  391. // made valid were removed from the working set before the
  392. // working set lock was acquired.
  393. //
  394. do {
  395. MiDoesPxeExistAndMakeValid (PointerPxe,
  396. TargetProcess,
  397. FALSE,
  398. &Waited);
  399. #if (_MI_PAGING_LEVELS >= 4)
  400. Waited = 0;
  401. #endif
  402. MiDoesPpeExistAndMakeValid (PointerPpe,
  403. TargetProcess,
  404. FALSE,
  405. &Waited);
  406. #if (_MI_PAGING_LEVELS < 4)
  407. Waited = 0;
  408. #endif
  409. MiDoesPdeExistAndMakeValid (PointerPde,
  410. TargetProcess,
  411. FALSE,
  412. &Waited);
  413. } while (Waited != 0);
  414. }
  415. //
  416. // The page is now in the working set, lock the page into
  417. // the working set.
  418. //
  419. PointerPte1 = MiGetPteAddress (Va);
  420. Pfn1 = MI_PFN_ELEMENT (PointerPte1->u.Hard.PageFrameNumber);
  421. Entry = MiLocateWsle (Va, MmWorkingSetList, Pfn1->u1.WsIndex);
  422. if (Entry >= MmWorkingSetList->FirstDynamic) {
  423. SwapEntry = MmWorkingSetList->FirstDynamic;
  424. if (Entry != MmWorkingSetList->FirstDynamic) {
  425. //
  426. // Swap this entry with the one at first dynamic.
  427. //
  428. MiSwapWslEntries (Entry, SwapEntry, &TargetProcess->Vm);
  429. }
  430. MmWorkingSetList->FirstDynamic += 1;
  431. } else {
  432. SwapEntry = Entry;
  433. }
  434. //
  435. // Indicate that the page is locked.
  436. //
  437. if (MapType & MAP_PROCESS) {
  438. MmWsle[SwapEntry].u1.e1.LockedInWs = 1;
  439. }
  440. if (MapType & MAP_SYSTEM) {
  441. MmWsle[SwapEntry].u1.e1.LockedInMemory = 1;
  442. }
  443. //
  444. // Increment to the next Va and PTE.
  445. //
  446. PointerPte += 1;
  447. Va = (PVOID)((PCHAR)Va + PAGE_SIZE);
  448. }
  449. UNLOCK_WS_UNSAFE (TargetProcess);
  450. SuccessReturn1:
  451. #if (defined(_MIALT4K_))
  452. if (Wow64Process != NULL) {
  453. MiLockFor4kPage (CapturedBase, CapturedRegionSize, TargetProcess);
  454. }
  455. #endif
  456. UNLOCK_ADDRESS_SPACE (TargetProcess);
  457. if (Attached == TRUE) {
  458. KeUnstackDetachProcess (&ApcState);
  459. }
  460. ObDereferenceObject (TargetProcess);
  461. //
  462. // Update return arguments.
  463. //
  464. //
  465. // Establish an exception handler and write the size and base
  466. // address.
  467. //
  468. try {
  469. #if defined(_MIALT4K_)
  470. if (Wow64Process != NULL) {
  471. *RegionSize = ((PCHAR)EndingAddress -
  472. (PCHAR)PAGE_4K_ALIGN(CapturedBase)) + PAGE_4K;
  473. *BaseAddress = PAGE_4K_ALIGN(CapturedBase);
  474. } else {
  475. #endif
  476. *RegionSize = ((PCHAR)EndingAddress - (PCHAR)PAGE_ALIGN(CapturedBase)) +
  477. PAGE_SIZE;
  478. *BaseAddress = PAGE_ALIGN(CapturedBase);
  479. #if defined(_MIALT4K_)
  480. }
  481. #endif
  482. } except (EXCEPTION_EXECUTE_HANDLER) {
  483. return GetExceptionCode();
  484. }
  485. if (WasLocked) {
  486. return STATUS_WAS_LOCKED;
  487. }
  488. return STATUS_SUCCESS;
  489. ErrorReturn:
  490. UNLOCK_WS_UNSAFE (TargetProcess);
  491. ErrorReturn1:
  492. UNLOCK_ADDRESS_SPACE (TargetProcess);
  493. if (Attached == TRUE) {
  494. KeUnstackDetachProcess (&ApcState);
  495. }
  496. ObDereferenceObject (TargetProcess);
  497. return Status;
  498. }
  499. NTSTATUS
  500. NtUnlockVirtualMemory (
  501. IN HANDLE ProcessHandle,
  502. IN OUT PVOID *BaseAddress,
  503. IN OUT PSIZE_T RegionSize,
  504. IN ULONG MapType
  505. )
  506. /*++
  507. Routine Description:
  508. This function unlocks a region of pages within the working set list
  509. of a subject process.
  510. As a side effect, any pages which are not locked and are in the
  511. process's working set are removed from the process's working set.
  512. This allows NtUnlockVirtualMemory to remove a range of pages
  513. from the working set.
  514. The caller of this function must have PROCESS_VM_OPERATION access
  515. to the target process.
  516. The caller must also have SeLockMemoryPrivilege for MAP_SYSTEM.
  517. Arguments:
  518. ProcessHandle - Supplies an open handle to a process object.
  519. BaseAddress - The base address of the region of pages
  520. to be unlocked. This value is rounded down to the
  521. next host page address boundary.
  522. RegionSize - A pointer to a variable that will receive
  523. the actual size in bytes of the unlocked region of
  524. pages. The initial value of this argument is
  525. rounded up to the next host page size boundary.
  526. MapType - A set of flags that describe the type of unlocking to
  527. perform. One of MAP_PROCESS or MAP_SYSTEM.
  528. Return Value:
  529. NTSTATUS.
  530. --*/
  531. {
  532. PVOID Va;
  533. PVOID EndingAddress;
  534. SIZE_T CapturedRegionSize;
  535. PVOID CapturedBase;
  536. PEPROCESS TargetProcess;
  537. NTSTATUS Status;
  538. KPROCESSOR_MODE PreviousMode;
  539. WSLE_NUMBER Entry;
  540. PMMPTE PointerPte;
  541. PMMPFN Pfn1;
  542. PMMVAD Vad;
  543. PVOID LastVa;
  544. LOGICAL Attached;
  545. KAPC_STATE ApcState;
  546. #if defined(_MIALT4K_)
  547. PVOID Wow64Process;
  548. #endif
  549. PAGED_CODE();
  550. LastVa = NULL;
  551. //
  552. // Validate the flags in MapType.
  553. //
  554. if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)) != 0) {
  555. return STATUS_INVALID_PARAMETER;
  556. }
  557. if ((MapType & (MAP_PROCESS | MAP_SYSTEM)) == 0) {
  558. return STATUS_INVALID_PARAMETER;
  559. }
  560. PreviousMode = KeGetPreviousMode();
  561. try {
  562. if (PreviousMode != KernelMode) {
  563. ProbeForWritePointer (BaseAddress);
  564. ProbeForWriteUlong_ptr (RegionSize);
  565. }
  566. //
  567. // Capture the base address.
  568. //
  569. CapturedBase = *BaseAddress;
  570. //
  571. // Capture the region size.
  572. //
  573. CapturedRegionSize = *RegionSize;
  574. } except (ExSystemExceptionFilter()) {
  575. //
  576. // If an exception occurs during the probe or capture
  577. // of the initial values, then handle the exception and
  578. // return the exception code as the status value.
  579. //
  580. return GetExceptionCode();
  581. }
  582. //
  583. // Make sure the specified starting and ending addresses are
  584. // within the user part of the virtual address space.
  585. //
  586. if (CapturedBase > MM_HIGHEST_USER_ADDRESS) {
  587. //
  588. // Invalid base address.
  589. //
  590. return STATUS_INVALID_PARAMETER;
  591. }
  592. if ((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (ULONG_PTR)CapturedBase <
  593. CapturedRegionSize) {
  594. //
  595. // Invalid region size;
  596. //
  597. return STATUS_INVALID_PARAMETER;
  598. }
  599. if (CapturedRegionSize == 0) {
  600. return STATUS_INVALID_PARAMETER;
  601. }
  602. Status = ObReferenceObjectByHandle ( ProcessHandle,
  603. PROCESS_VM_OPERATION,
  604. PsProcessType,
  605. PreviousMode,
  606. (PVOID *)&TargetProcess,
  607. NULL );
  608. if (!NT_SUCCESS(Status)) {
  609. return Status;
  610. }
  611. #if defined(_MIALT4K_)
  612. Wow64Process = TargetProcess->Wow64Process;
  613. #endif
  614. if ((MapType & MAP_SYSTEM) != 0) {
  615. //
  616. // In addition to PROCESS_VM_OPERATION access to the target
  617. // process, the caller must have SE_LOCK_MEMORY_PRIVILEGE.
  618. //
  619. if (!SeSinglePrivilegeCheck(
  620. SeLockMemoryPrivilege,
  621. PreviousMode
  622. )) {
  623. ObDereferenceObject( TargetProcess );
  624. return STATUS_PRIVILEGE_NOT_HELD;
  625. }
  626. }
  627. //
  628. // Attach to the specified process.
  629. //
  630. if (ProcessHandle != NtCurrentProcess()) {
  631. KeStackAttachProcess (&TargetProcess->Pcb, &ApcState);
  632. Attached = TRUE;
  633. }
  634. else {
  635. Attached = FALSE;
  636. }
  637. EndingAddress = PAGE_ALIGN((PCHAR)CapturedBase + CapturedRegionSize - 1);
  638. Va = PAGE_ALIGN (CapturedBase);
  639. //
  640. // Get address creation mutex, this prevents the
  641. // address range from being modified while it is examined.
  642. // Block APCs so an APC routine can't get a page fault and
  643. // corrupt the working set list, etc.
  644. //
  645. LOCK_ADDRESS_SPACE (TargetProcess);
  646. //
  647. // Make sure the address space was not deleted, if so, return an error.
  648. //
  649. if (TargetProcess->Flags & PS_PROCESS_FLAGS_VM_DELETED) {
  650. Status = STATUS_PROCESS_IS_TERMINATING;
  651. goto ErrorReturn1;
  652. }
  653. LOCK_WS_UNSAFE (TargetProcess);
  654. while (Va <= EndingAddress) {
  655. //
  656. // Check to ensure all the specified pages are locked.
  657. //
  658. if (Va > LastVa) {
  659. Vad = MiLocateAddress (Va);
  660. if (Vad == NULL) {
  661. Va = (PVOID)((PCHAR)Va + PAGE_SIZE);
  662. Status = STATUS_NOT_LOCKED;
  663. break;
  664. }
  665. //
  666. // Don't unlock physically mapped views.
  667. //
  668. if ((Vad->u.VadFlags.PhysicalMapping == 1) ||
  669. (Vad->u.VadFlags.UserPhysicalPages == 1)) {
  670. Va = MI_VPN_TO_VA (Vad->EndingVpn);
  671. break;
  672. }
  673. LastVa = MI_VPN_TO_VA (Vad->EndingVpn);
  674. }
  675. if (!MmIsAddressValid (Va)) {
  676. //
  677. // This page is not valid, therefore not in working set.
  678. //
  679. Status = STATUS_NOT_LOCKED;
  680. }
  681. else {
  682. PointerPte = MiGetPteAddress (Va);
  683. ASSERT (PointerPte->u.Hard.Valid != 0);
  684. Pfn1 = MI_PFN_ELEMENT (PointerPte->u.Hard.PageFrameNumber);
  685. Entry = MiLocateWsle (Va, MmWorkingSetList, Pfn1->u1.WsIndex);
  686. ASSERT (Entry != WSLE_NULL_INDEX);
  687. if ((MmWsle[Entry].u1.e1.LockedInWs == 0) &&
  688. (MmWsle[Entry].u1.e1.LockedInMemory == 0)) {
  689. //
  690. // Not locked in memory or system, remove from working
  691. // set.
  692. //
  693. PERFINFO_PAGE_INFO_DECL();
  694. PERFINFO_GET_PAGE_INFO(PointerPte);
  695. if (MiFreeWsle (Entry, &TargetProcess->Vm, PointerPte)) {
  696. PERFINFO_LOG_WS_REMOVAL(PERFINFO_LOG_TYPE_OUTWS_EMPTYQ, &TargetProcess->Vm);
  697. }
  698. Status = STATUS_NOT_LOCKED;
  699. } else if (MapType & MAP_PROCESS) {
  700. if (MmWsle[Entry].u1.e1.LockedInWs == 0) {
  701. //
  702. // This page is not locked.
  703. //
  704. Status = STATUS_NOT_LOCKED;
  705. }
  706. } else {
  707. if (MmWsle[Entry].u1.e1.LockedInMemory == 0) {
  708. //
  709. // This page is not locked.
  710. //
  711. Status = STATUS_NOT_LOCKED;
  712. }
  713. }
  714. }
  715. Va = (PVOID)((PCHAR)Va + PAGE_SIZE);
  716. }
  717. #if defined(_MIALT4K_)
  718. if (Wow64Process != NULL) {
  719. //
  720. // This call may release and reacquire the working set mutex !!!
  721. //
  722. // Therefore the loop following must handle PTEs which have been
  723. // trimmed during this window.
  724. //
  725. Status = MiUnlockFor4kPage (CapturedBase,
  726. CapturedRegionSize,
  727. TargetProcess);
  728. }
  729. #endif
  730. if (Status == STATUS_NOT_LOCKED) {
  731. goto ErrorReturn;
  732. }
  733. //
  734. // The complete address range is locked, unlock them.
  735. //
  736. Va = PAGE_ALIGN (CapturedBase);
  737. LastVa = NULL;
  738. while (Va <= EndingAddress) {
  739. #if defined(_MIALT4K_)
  740. if (Wow64Process != NULL) {
  741. //
  742. // This call may release and reacquire the working set mutex !!!
  743. //
  744. // Therefore the code below must handle PTEs which have been
  745. // trimmed during this window.
  746. //
  747. if (!MiShouldBeUnlockedFor4kPage(Va, TargetProcess)) {
  748. //
  749. // The other 4k pages in the native page still hold
  750. // the page lock. Should skip unlocking.
  751. //
  752. Va = (PVOID)((PCHAR)Va + PAGE_SIZE);
  753. continue;
  754. }
  755. }
  756. #endif
  757. //
  758. // Don't unlock physically mapped views.
  759. //
  760. if (Va > LastVa) {
  761. Vad = MiLocateAddress (Va);
  762. ASSERT (Vad != NULL);
  763. if ((Vad->u.VadFlags.PhysicalMapping == 1) ||
  764. (Vad->u.VadFlags.UserPhysicalPages == 1)) {
  765. Va = MI_VPN_TO_VA (Vad->EndingVpn);
  766. break;
  767. }
  768. LastVa = MI_VPN_TO_VA (Vad->EndingVpn);
  769. }
  770. #if defined(_MIALT4K_)
  771. if (!MmIsAddressValid (Va)) {
  772. //
  773. // The page or any mapping table page may have been trimmed when
  774. // MiUnlockFor4kPage or MiShouldBeUnlockedFor4kPage released the
  775. // working set mutex. If this has occurred, then clearly the
  776. // address is no longer locked so just skip it.
  777. //
  778. Va = (PVOID)((PCHAR)Va + PAGE_SIZE);
  779. continue;
  780. }
  781. #endif
  782. PointerPte = MiGetPteAddress (Va);
  783. ASSERT (PointerPte->u.Hard.Valid == 1);
  784. Pfn1 = MI_PFN_ELEMENT (PointerPte->u.Hard.PageFrameNumber);
  785. Entry = MiLocateWsle (Va, MmWorkingSetList, Pfn1->u1.WsIndex);
  786. if (MapType & MAP_PROCESS) {
  787. MmWsle[Entry].u1.e1.LockedInWs = 0;
  788. }
  789. if (MapType & MAP_SYSTEM) {
  790. MmWsle[Entry].u1.e1.LockedInMemory = 0;
  791. }
  792. if ((MmWsle[Entry].u1.e1.LockedInMemory == 0) &&
  793. MmWsle[Entry].u1.e1.LockedInWs == 0) {
  794. //
  795. // The page is no longer should be locked, move
  796. // it to the dynamic part of the working set.
  797. //
  798. MmWorkingSetList->FirstDynamic -= 1;
  799. if (Entry != MmWorkingSetList->FirstDynamic) {
  800. //
  801. // Swap this element with the last locked page, making
  802. // this element the new first dynamic entry.
  803. //
  804. MiSwapWslEntries (Entry,
  805. MmWorkingSetList->FirstDynamic,
  806. &TargetProcess->Vm);
  807. }
  808. }
  809. Va = (PVOID)((PCHAR)Va + PAGE_SIZE);
  810. }
  811. UNLOCK_WS_AND_ADDRESS_SPACE (TargetProcess);
  812. if (Attached == TRUE) {
  813. KeUnstackDetachProcess (&ApcState);
  814. }
  815. ObDereferenceObject (TargetProcess);
  816. //
  817. // Update return arguments.
  818. //
  819. // Establish an exception handler and write the size and base
  820. // address.
  821. //
  822. try {
  823. #if defined(_MIALT4K_)
  824. if (Wow64Process != NULL) {
  825. *RegionSize = ((PCHAR)EndingAddress -
  826. (PCHAR)PAGE_4K_ALIGN(CapturedBase)) + PAGE_4K;
  827. *BaseAddress = PAGE_4K_ALIGN(CapturedBase);
  828. } else {
  829. #endif
  830. *RegionSize = ((PCHAR)EndingAddress -
  831. (PCHAR)PAGE_ALIGN(CapturedBase)) + PAGE_SIZE;
  832. *BaseAddress = PAGE_ALIGN(CapturedBase);
  833. #if defined(_MIALT4K_)
  834. }
  835. #endif
  836. } except (EXCEPTION_EXECUTE_HANDLER) {
  837. return GetExceptionCode();
  838. }
  839. return STATUS_SUCCESS;
  840. ErrorReturn:
  841. UNLOCK_WS_UNSAFE (TargetProcess);
  842. ErrorReturn1:
  843. UNLOCK_ADDRESS_SPACE (TargetProcess);
  844. if (Attached == TRUE) {
  845. KeUnstackDetachProcess (&ApcState);
  846. }
  847. ObDereferenceObject (TargetProcess);
  848. return Status;
  849. }