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.

1192 lines
35 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. queryvm.c
  5. Abstract:
  6. This module contains the routines which implement the
  7. NtQueryVirtualMemory service.
  8. Author:
  9. Lou Perazzoli (loup) 21-Aug-1989
  10. Landy Wang (landyw) 02-June-1997
  11. Revision History:
  12. --*/
  13. #include "mi.h"
  14. extern POBJECT_TYPE IoFileObjectType;
  15. NTSTATUS
  16. MiGetWorkingSetInfo (
  17. IN PMEMORY_WORKING_SET_INFORMATION WorkingSetInfo,
  18. IN SIZE_T Length,
  19. IN PEPROCESS Process
  20. );
  21. MMPTE
  22. MiCaptureSystemPte (
  23. IN PMMPTE PointerProtoPte,
  24. IN PEPROCESS Process
  25. );
  26. #if DBG
  27. PEPROCESS MmWatchProcess;
  28. #endif // DBG
  29. ULONG
  30. MiQueryAddressState (
  31. IN PVOID Va,
  32. IN PMMVAD Vad,
  33. IN PEPROCESS TargetProcess,
  34. OUT PULONG ReturnedProtect,
  35. OUT PVOID *NextVaToQuery
  36. );
  37. #ifdef ALLOC_PRAGMA
  38. #pragma alloc_text(PAGE,NtQueryVirtualMemory)
  39. #pragma alloc_text(PAGE,MiQueryAddressState)
  40. #pragma alloc_text(PAGE,MiGetWorkingSetInfo)
  41. #endif
  42. NTSTATUS
  43. NtQueryVirtualMemory (
  44. IN HANDLE ProcessHandle,
  45. IN PVOID BaseAddress,
  46. IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
  47. OUT PVOID MemoryInformation,
  48. IN SIZE_T MemoryInformationLength,
  49. OUT PSIZE_T ReturnLength OPTIONAL
  50. )
  51. /*++
  52. Routine Description:
  53. This function provides the capability to determine the state,
  54. protection, and type of a region of pages within the virtual address
  55. space of the subject process.
  56. The state of the first page within the region is determined and then
  57. subsequent entries in the process address map are scanned from the
  58. base address upward until either the entire range of pages has been
  59. scanned or until a page with a nonmatching set of attributes is
  60. encountered. The region attributes, the length of the region of pages
  61. with matching attributes, and an appropriate status value are
  62. returned.
  63. If the entire region of pages does not have a matching set of
  64. attributes, then the returned length parameter value can be used to
  65. calculate the address and length of the region of pages that was not
  66. scanned.
  67. Arguments:
  68. ProcessHandle - An open handle to a process object.
  69. BaseAddress - The base address of the region of pages to be
  70. queried. This value is rounded down to the next host-page-
  71. address boundary.
  72. MemoryInformationClass - The memory information class about which
  73. to retrieve information.
  74. MemoryInformation - A pointer to a buffer that receives the specified
  75. information. The format and content of the buffer
  76. depend on the specified information class.
  77. MemoryBasicInformation - Data type is PMEMORY_BASIC_INFORMATION.
  78. MEMORY_BASIC_INFORMATION Structure
  79. ULONG RegionSize - The size of the region in bytes beginning at
  80. the base address in which all pages have
  81. identical attributes.
  82. ULONG State - The state of the pages within the region.
  83. State Values
  84. MEM_COMMIT - The state of the pages within the region
  85. is committed.
  86. MEM_FREE - The state of the pages within the region
  87. is free.
  88. MEM_RESERVE - The state of the pages within the
  89. region is reserved.
  90. ULONG Protect - The protection of the pages within the region.
  91. Protect Values
  92. PAGE_NOACCESS - No access to the region of pages is allowed.
  93. An attempt to read, write, or execute within
  94. the region results in an access violation.
  95. PAGE_EXECUTE - Execute access to the region of pages
  96. is allowed. An attempt to read or write within
  97. the region results in an access violation.
  98. PAGE_READONLY - Read-only and execute access to the region
  99. of pages is allowed. An attempt to write within
  100. the region results in an access violation.
  101. PAGE_READWRITE - Read, write, and execute access to the region
  102. of pages is allowed. If write access to the
  103. underlying section is allowed, then a single
  104. copy of the pages are shared. Otherwise,
  105. the pages are shared read-only/copy-on-write.
  106. PAGE_GUARD - Read, write, and execute access to the
  107. region of pages is allowed; however, access to
  108. the region causes a "guard region entered"
  109. condition to be raised in the subject process.
  110. PAGE_NOCACHE - Disable the placement of committed
  111. pages into the data cache.
  112. ULONG Type - The type of pages within the region.
  113. Type Values
  114. MEM_PRIVATE - The pages within the region are private.
  115. MEM_MAPPED - The pages within the region are mapped
  116. into the view of a section.
  117. MEM_IMAGE - The pages within the region are mapped
  118. into the view of an image section.
  119. MemoryInformationLength - Specifies the length in bytes of
  120. the memory information buffer.
  121. ReturnLength - An optional pointer which, if specified, receives the
  122. number of bytes placed in the process information buffer.
  123. Return Value:
  124. NTSTATUS.
  125. Environment:
  126. Kernel mode.
  127. --*/
  128. {
  129. KPROCESSOR_MODE PreviousMode;
  130. PEPROCESS TargetProcess;
  131. NTSTATUS Status;
  132. PMMVAD Vad;
  133. PVOID Va;
  134. PVOID NextVaToQuery;
  135. LOGICAL Found;
  136. SIZE_T TheRegionSize;
  137. ULONG NewProtect;
  138. ULONG NewState;
  139. PVOID FilePointer;
  140. ULONG_PTR BaseVpn;
  141. MEMORY_BASIC_INFORMATION Info;
  142. PMEMORY_BASIC_INFORMATION BasicInfo;
  143. LOGICAL Attached;
  144. LOGICAL Leaped;
  145. ULONG MemoryInformationLengthUlong;
  146. KAPC_STATE ApcState;
  147. PETHREAD CurrentThread;
  148. PVOID HighestVadAddress;
  149. PVOID HighestUserAddress;
  150. Found = FALSE;
  151. Leaped = TRUE;
  152. FilePointer = NULL;
  153. //
  154. // Make sure the user's buffer is large enough for the requested operation.
  155. //
  156. // Check argument validity.
  157. //
  158. switch (MemoryInformationClass) {
  159. case MemoryBasicInformation:
  160. if (MemoryInformationLength < sizeof(MEMORY_BASIC_INFORMATION)) {
  161. return STATUS_INFO_LENGTH_MISMATCH;
  162. }
  163. break;
  164. case MemoryWorkingSetInformation:
  165. if (MemoryInformationLength < sizeof(ULONG_PTR)) {
  166. return STATUS_INFO_LENGTH_MISMATCH;
  167. }
  168. break;
  169. case MemoryMappedFilenameInformation:
  170. break;
  171. default:
  172. return STATUS_INVALID_INFO_CLASS;
  173. }
  174. CurrentThread = PsGetCurrentThread ();
  175. PreviousMode = KeGetPreviousModeByThread(&CurrentThread->Tcb);
  176. if (PreviousMode != KernelMode) {
  177. //
  178. // Check arguments.
  179. //
  180. try {
  181. ProbeForWrite(MemoryInformation,
  182. MemoryInformationLength,
  183. sizeof(ULONG_PTR));
  184. if (ARGUMENT_PRESENT(ReturnLength)) {
  185. ProbeForWriteUlong_ptr(ReturnLength);
  186. }
  187. } except (EXCEPTION_EXECUTE_HANDLER) {
  188. //
  189. // If an exception occurs during the probe or capture
  190. // of the initial values, then handle the exception and
  191. // return the exception code as the status value.
  192. //
  193. return GetExceptionCode();
  194. }
  195. }
  196. if (BaseAddress > MM_HIGHEST_USER_ADDRESS) {
  197. return STATUS_INVALID_PARAMETER;
  198. }
  199. HighestUserAddress = MM_HIGHEST_USER_ADDRESS;
  200. HighestVadAddress = (PCHAR) MM_HIGHEST_VAD_ADDRESS;
  201. #if defined(_WIN64)
  202. if (ProcessHandle == NtCurrentProcess()) {
  203. TargetProcess = PsGetCurrentProcessByThread(CurrentThread);
  204. }
  205. else {
  206. Status = ObReferenceObjectByHandle (ProcessHandle,
  207. PROCESS_QUERY_INFORMATION,
  208. PsProcessType,
  209. PreviousMode,
  210. (PVOID *)&TargetProcess,
  211. NULL);
  212. if (!NT_SUCCESS(Status)) {
  213. return Status;
  214. }
  215. }
  216. //
  217. // If this is a wow64 process, then return the appropriate highest
  218. // user address depending on whether the process has been started with
  219. // a 2GB or a 4GB address space.
  220. //
  221. if (TargetProcess->Wow64Process != NULL) {
  222. if (TargetProcess->Flags & PS_PROCESS_FLAGS_WOW64_4GB_VA_SPACE) {
  223. HighestUserAddress = (PVOID) ((ULONG_PTR)_4gb - X64K - 1);
  224. }
  225. else {
  226. HighestUserAddress = (PVOID) ((ULONG_PTR)_2gb - X64K - 1);
  227. }
  228. HighestVadAddress = (PCHAR)HighestUserAddress - X64K;
  229. if (BaseAddress > HighestUserAddress) {
  230. if (ProcessHandle != NtCurrentProcess()) {
  231. ObDereferenceObject (TargetProcess);
  232. }
  233. return STATUS_INVALID_PARAMETER;
  234. }
  235. }
  236. #endif
  237. if ((BaseAddress >= HighestVadAddress) ||
  238. (PAGE_ALIGN(BaseAddress) == (PVOID)MM_SHARED_USER_DATA_VA)) {
  239. //
  240. // Indicate a reserved area from this point on.
  241. //
  242. Status = STATUS_INVALID_ADDRESS;
  243. if (MemoryInformationClass == MemoryBasicInformation) {
  244. try {
  245. ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->AllocationBase =
  246. (PCHAR) HighestVadAddress + 1;
  247. ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->AllocationProtect =
  248. PAGE_READONLY;
  249. ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->BaseAddress =
  250. PAGE_ALIGN(BaseAddress);
  251. ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->RegionSize =
  252. ((PCHAR)HighestUserAddress + 1) -
  253. (PCHAR)PAGE_ALIGN(BaseAddress);
  254. ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->State = MEM_RESERVE;
  255. ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->Protect = PAGE_NOACCESS;
  256. ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->Type = MEM_PRIVATE;
  257. if (ARGUMENT_PRESENT(ReturnLength)) {
  258. *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
  259. }
  260. if (PAGE_ALIGN(BaseAddress) == (PVOID)MM_SHARED_USER_DATA_VA) {
  261. //
  262. // This is the page that is double mapped between
  263. // user mode and kernel mode.
  264. //
  265. ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->AllocationBase =
  266. (PVOID)MM_SHARED_USER_DATA_VA;
  267. ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->Protect =
  268. PAGE_READONLY;
  269. ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->RegionSize =
  270. PAGE_SIZE;
  271. ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->State =
  272. MEM_COMMIT;
  273. }
  274. } except (EXCEPTION_EXECUTE_HANDLER) {
  275. //
  276. // Just return success.
  277. //
  278. NOTHING;
  279. }
  280. Status = STATUS_SUCCESS;
  281. }
  282. #if defined(_WIN64)
  283. if (ProcessHandle != NtCurrentProcess()) {
  284. ObDereferenceObject (TargetProcess);
  285. }
  286. #endif
  287. return Status;
  288. }
  289. #if !defined(_WIN64)
  290. if (ProcessHandle == NtCurrentProcess()) {
  291. TargetProcess = PsGetCurrentProcessByThread(CurrentThread);
  292. }
  293. else {
  294. Status = ObReferenceObjectByHandle (ProcessHandle,
  295. PROCESS_QUERY_INFORMATION,
  296. PsProcessType,
  297. PreviousMode,
  298. (PVOID *)&TargetProcess,
  299. NULL);
  300. if (!NT_SUCCESS(Status)) {
  301. return Status;
  302. }
  303. }
  304. #endif
  305. if (MemoryInformationClass == MemoryWorkingSetInformation) {
  306. Status = MiGetWorkingSetInfo (
  307. (PMEMORY_WORKING_SET_INFORMATION) MemoryInformation,
  308. MemoryInformationLength,
  309. TargetProcess);
  310. if (ProcessHandle != NtCurrentProcess()) {
  311. ObDereferenceObject (TargetProcess);
  312. }
  313. //
  314. // If MiGetWorkingSetInfo failed then inform the caller.
  315. //
  316. if (!NT_SUCCESS(Status)) {
  317. return Status;
  318. }
  319. try {
  320. if (ARGUMENT_PRESENT(ReturnLength)) {
  321. *ReturnLength = ((((PMEMORY_WORKING_SET_INFORMATION)
  322. MemoryInformation)->NumberOfEntries - 1) *
  323. sizeof(ULONG_PTR)) +
  324. sizeof(MEMORY_WORKING_SET_INFORMATION);
  325. }
  326. } except (EXCEPTION_EXECUTE_HANDLER) {
  327. }
  328. return STATUS_SUCCESS;
  329. }
  330. //
  331. // If the specified process is not the current process, attach
  332. // to the specified process.
  333. //
  334. if (ProcessHandle != NtCurrentProcess()) {
  335. KeStackAttachProcess (&TargetProcess->Pcb, &ApcState);
  336. Attached = TRUE;
  337. }
  338. else {
  339. Attached = FALSE;
  340. }
  341. //
  342. // Get working set mutex and block APCs.
  343. //
  344. LOCK_ADDRESS_SPACE (TargetProcess);
  345. //
  346. // Make sure the address space was not deleted, if so, return an error.
  347. //
  348. if (TargetProcess->Flags & PS_PROCESS_FLAGS_VM_DELETED) {
  349. UNLOCK_ADDRESS_SPACE (TargetProcess);
  350. if (Attached == TRUE) {
  351. KeUnstackDetachProcess (&ApcState);
  352. ObDereferenceObject (TargetProcess);
  353. }
  354. return STATUS_PROCESS_IS_TERMINATING;
  355. }
  356. //
  357. // Locate the VAD that contains the base address or the VAD
  358. // which follows the base address.
  359. //
  360. if (TargetProcess->VadRoot.NumberGenericTableElements != 0) {
  361. Vad = (PMMVAD) TargetProcess->VadRoot.BalancedRoot.RightChild;
  362. BaseVpn = MI_VA_TO_VPN (BaseAddress);
  363. while (TRUE) {
  364. if (Vad == NULL) {
  365. break;
  366. }
  367. if ((BaseVpn >= Vad->StartingVpn) &&
  368. (BaseVpn <= Vad->EndingVpn)) {
  369. Found = TRUE;
  370. break;
  371. }
  372. if (BaseVpn < Vad->StartingVpn) {
  373. if (Vad->LeftChild == NULL) {
  374. break;
  375. }
  376. Vad = Vad->LeftChild;
  377. }
  378. else {
  379. if (BaseVpn < Vad->EndingVpn) {
  380. break;
  381. }
  382. if (Vad->RightChild == NULL) {
  383. break;
  384. }
  385. Vad = Vad->RightChild;
  386. }
  387. }
  388. }
  389. else {
  390. Vad = NULL;
  391. BaseVpn = 0;
  392. }
  393. if (!Found) {
  394. //
  395. // There is no virtual address allocated at the base
  396. // address. Return the size of the hole starting at
  397. // the base address.
  398. //
  399. if (Vad == NULL) {
  400. TheRegionSize = (((PCHAR)HighestVadAddress + 1) -
  401. (PCHAR)PAGE_ALIGN(BaseAddress));
  402. }
  403. else {
  404. if (Vad->StartingVpn < BaseVpn) {
  405. //
  406. // We are looking at the Vad which occupies the range
  407. // just before the desired range. Get the next Vad.
  408. //
  409. Vad = MiGetNextVad (Vad);
  410. if (Vad == NULL) {
  411. TheRegionSize = (((PCHAR)HighestVadAddress + 1) -
  412. (PCHAR)PAGE_ALIGN(BaseAddress));
  413. }
  414. else {
  415. TheRegionSize = (PCHAR)MI_VPN_TO_VA (Vad->StartingVpn) -
  416. (PCHAR)PAGE_ALIGN(BaseAddress);
  417. }
  418. }
  419. else {
  420. TheRegionSize = (PCHAR)MI_VPN_TO_VA (Vad->StartingVpn) -
  421. (PCHAR)PAGE_ALIGN(BaseAddress);
  422. }
  423. }
  424. UNLOCK_ADDRESS_SPACE (TargetProcess);
  425. if (Attached == TRUE) {
  426. KeUnstackDetachProcess (&ApcState);
  427. ObDereferenceObject (TargetProcess);
  428. }
  429. //
  430. // Establish an exception handler and write the information and
  431. // returned length.
  432. //
  433. if (MemoryInformationClass == MemoryBasicInformation) {
  434. BasicInfo = (PMEMORY_BASIC_INFORMATION) MemoryInformation;
  435. Found = FALSE;
  436. try {
  437. BasicInfo->AllocationBase = NULL;
  438. BasicInfo->AllocationProtect = 0;
  439. BasicInfo->BaseAddress = PAGE_ALIGN(BaseAddress);
  440. BasicInfo->RegionSize = TheRegionSize;
  441. BasicInfo->State = MEM_FREE;
  442. BasicInfo->Protect = PAGE_NOACCESS;
  443. BasicInfo->Type = 0;
  444. Found = TRUE;
  445. if (ARGUMENT_PRESENT(ReturnLength)) {
  446. *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
  447. }
  448. } except (EXCEPTION_EXECUTE_HANDLER) {
  449. //
  450. // Just return success if the BasicInfo was successfully
  451. // filled in.
  452. //
  453. if (Found == FALSE) {
  454. return GetExceptionCode ();
  455. }
  456. }
  457. return STATUS_SUCCESS;
  458. }
  459. return STATUS_INVALID_ADDRESS;
  460. }
  461. //
  462. // Found a VAD.
  463. //
  464. Va = PAGE_ALIGN(BaseAddress);
  465. Info.BaseAddress = Va;
  466. //
  467. // There is a page mapped at the base address.
  468. //
  469. if (Vad->u.VadFlags.PrivateMemory) {
  470. Info.Type = MEM_PRIVATE;
  471. }
  472. else {
  473. if (Vad->u.VadFlags.ImageMap == 1) {
  474. Info.Type = MEM_IMAGE;
  475. }
  476. else {
  477. Info.Type = MEM_MAPPED;
  478. }
  479. if (MemoryInformationClass == MemoryMappedFilenameInformation) {
  480. if (Vad->ControlArea != NULL) {
  481. FilePointer = Vad->ControlArea->FilePointer;
  482. }
  483. if (FilePointer == NULL) {
  484. FilePointer = (PVOID)1;
  485. }
  486. else {
  487. ObReferenceObject (FilePointer);
  488. }
  489. }
  490. }
  491. LOCK_WS_UNSAFE (TargetProcess);
  492. Info.State = MiQueryAddressState (Va,
  493. Vad,
  494. TargetProcess,
  495. &Info.Protect,
  496. &NextVaToQuery);
  497. Va = NextVaToQuery;
  498. while (MI_VA_TO_VPN (Va) <= Vad->EndingVpn) {
  499. NewState = MiQueryAddressState (Va,
  500. Vad,
  501. TargetProcess,
  502. &NewProtect,
  503. &NextVaToQuery);
  504. if ((NewState != Info.State) || (NewProtect != Info.Protect)) {
  505. //
  506. // The state for this address does not match, calculate
  507. // size and return.
  508. //
  509. Leaped = FALSE;
  510. break;
  511. }
  512. Va = NextVaToQuery;
  513. }
  514. UNLOCK_WS_UNSAFE (TargetProcess);
  515. //
  516. // We may have aggressively leaped past the end of the VAD. Shorten the
  517. // Va here if we did.
  518. //
  519. if (Leaped == TRUE) {
  520. Va = MI_VPN_TO_VA (Vad->EndingVpn + 1);
  521. }
  522. Info.RegionSize = ((PCHAR)Va - (PCHAR)Info.BaseAddress);
  523. Info.AllocationBase = MI_VPN_TO_VA (Vad->StartingVpn);
  524. Info.AllocationProtect = MI_CONVERT_FROM_PTE_PROTECTION (
  525. Vad->u.VadFlags.Protection);
  526. //
  527. // A range has been found, release the mutexes, detach from the
  528. // target process and return the information.
  529. //
  530. #if defined(_MIALT4K_)
  531. if (TargetProcess->Wow64Process != NULL) {
  532. Info.BaseAddress = PAGE_4K_ALIGN(BaseAddress);
  533. MiQueryRegionFor4kPage (Info.BaseAddress,
  534. MI_VPN_TO_VA_ENDING(Vad->EndingVpn),
  535. &Info.RegionSize,
  536. &Info.State,
  537. &Info.Protect,
  538. TargetProcess);
  539. }
  540. #endif
  541. UNLOCK_ADDRESS_SPACE (TargetProcess);
  542. if (Attached == TRUE) {
  543. KeUnstackDetachProcess (&ApcState);
  544. ObDereferenceObject (TargetProcess);
  545. }
  546. if (MemoryInformationClass == MemoryBasicInformation) {
  547. Found = FALSE;
  548. try {
  549. *(PMEMORY_BASIC_INFORMATION)MemoryInformation = Info;
  550. Found = TRUE;
  551. if (ARGUMENT_PRESENT(ReturnLength)) {
  552. *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
  553. }
  554. } except (EXCEPTION_EXECUTE_HANDLER) {
  555. //
  556. // Just return success if the BasicInfo was successfully
  557. // filled in.
  558. //
  559. if (Found == FALSE) {
  560. return GetExceptionCode ();
  561. }
  562. }
  563. return STATUS_SUCCESS;
  564. }
  565. //
  566. // Try to return the name of the file that is mapped.
  567. //
  568. if (FilePointer == NULL) {
  569. return STATUS_INVALID_ADDRESS;
  570. }
  571. if (FilePointer == (PVOID)1) {
  572. return STATUS_FILE_INVALID;
  573. }
  574. MemoryInformationLengthUlong = (ULONG)MemoryInformationLength;
  575. if ((SIZE_T)MemoryInformationLengthUlong < MemoryInformationLength) {
  576. return STATUS_INVALID_PARAMETER_5;
  577. }
  578. //
  579. // We have a referenced pointer to the file. Call ObQueryNameString
  580. // and get the file name.
  581. //
  582. Status = ObQueryNameString (FilePointer,
  583. (POBJECT_NAME_INFORMATION) MemoryInformation,
  584. MemoryInformationLengthUlong,
  585. (PULONG)ReturnLength);
  586. ObDereferenceObject (FilePointer);
  587. return Status;
  588. }
  589. ULONG
  590. MiQueryAddressState (
  591. IN PVOID Va,
  592. IN PMMVAD Vad,
  593. IN PEPROCESS TargetProcess,
  594. OUT PULONG ReturnedProtect,
  595. OUT PVOID *NextVaToQuery
  596. )
  597. /*++
  598. Routine Description:
  599. Arguments:
  600. Return Value:
  601. Returns the state (MEM_COMMIT, MEM_RESERVE, MEM_PRIVATE).
  602. Environment:
  603. Kernel mode. Working set lock and address creation lock held.
  604. --*/
  605. {
  606. PMMPTE PointerPte;
  607. PMMPTE PointerPde;
  608. PMMPTE PointerPpe;
  609. PMMPTE PointerPxe;
  610. MMPTE CapturedProtoPte;
  611. PMMPTE ProtoPte;
  612. LOGICAL PteIsZero;
  613. ULONG State;
  614. ULONG Protect;
  615. ULONG Waited;
  616. LOGICAL PteDetected;
  617. PVOID NextVa;
  618. State = MEM_RESERVE;
  619. Protect = 0;
  620. PointerPxe = MiGetPxeAddress (Va);
  621. PointerPpe = MiGetPpeAddress (Va);
  622. PointerPde = MiGetPdeAddress (Va);
  623. PointerPte = MiGetPteAddress (Va);
  624. ASSERT ((Vad->StartingVpn <= MI_VA_TO_VPN (Va)) &&
  625. (Vad->EndingVpn >= MI_VA_TO_VPN (Va)));
  626. PteIsZero = TRUE;
  627. PteDetected = FALSE;
  628. *NextVaToQuery = (PVOID)((PCHAR)Va + PAGE_SIZE);
  629. do {
  630. if (!MiDoesPxeExistAndMakeValid (PointerPxe,
  631. TargetProcess,
  632. MM_NOIRQL,
  633. &Waited)) {
  634. #if (_MI_PAGING_LEVELS >= 4)
  635. NextVa = MiGetVirtualAddressMappedByPte (PointerPxe + 1);
  636. NextVa = MiGetVirtualAddressMappedByPte (NextVa);
  637. NextVa = MiGetVirtualAddressMappedByPte (NextVa);
  638. *NextVaToQuery = MiGetVirtualAddressMappedByPte (NextVa);
  639. #endif
  640. break;
  641. }
  642. #if (_MI_PAGING_LEVELS >= 4)
  643. Waited = 0;
  644. #endif
  645. if (!MiDoesPpeExistAndMakeValid (PointerPpe,
  646. TargetProcess,
  647. MM_NOIRQL,
  648. &Waited)) {
  649. #if (_MI_PAGING_LEVELS >= 3)
  650. NextVa = MiGetVirtualAddressMappedByPte (PointerPpe + 1);
  651. NextVa = MiGetVirtualAddressMappedByPte (NextVa);
  652. *NextVaToQuery = MiGetVirtualAddressMappedByPte (NextVa);
  653. #endif
  654. break;
  655. }
  656. #if (_MI_PAGING_LEVELS < 4)
  657. Waited = 0;
  658. #endif
  659. if (!MiDoesPdeExistAndMakeValid (PointerPde,
  660. TargetProcess,
  661. MM_NOIRQL,
  662. &Waited)) {
  663. NextVa = MiGetVirtualAddressMappedByPte (PointerPde + 1);
  664. *NextVaToQuery = MiGetVirtualAddressMappedByPte (NextVa);
  665. break;
  666. }
  667. if (Waited == 0) {
  668. PteDetected = TRUE;
  669. }
  670. } while (Waited != 0);
  671. if (PteDetected == TRUE) {
  672. //
  673. // A PTE exists at this address, see if it is zero.
  674. //
  675. if (MI_PDE_MAPS_LARGE_PAGE (PointerPde)) {
  676. *ReturnedProtect = PAGE_READWRITE;
  677. NextVa = MiGetVirtualAddressMappedByPte (PointerPde + 1);
  678. *NextVaToQuery = MiGetVirtualAddressMappedByPte (NextVa);
  679. return MEM_COMMIT;
  680. }
  681. if (PointerPte->u.Long != 0) {
  682. PteIsZero = FALSE;
  683. //
  684. // There is a non-zero PTE at this address, use
  685. // it to build the information block.
  686. //
  687. if (MiIsPteDecommittedPage (PointerPte)) {
  688. ASSERT (Protect == 0);
  689. ASSERT (State == MEM_RESERVE);
  690. }
  691. else {
  692. State = MEM_COMMIT;
  693. if (Vad->u.VadFlags.PhysicalMapping == 1) {
  694. //
  695. // Physical mapping, there is no corresponding
  696. // PFN element to get the page protection from.
  697. //
  698. Protect = MI_CONVERT_FROM_PTE_PROTECTION (
  699. Vad->u.VadFlags.Protection);
  700. }
  701. else {
  702. Protect = MiGetPageProtection (PointerPte,
  703. TargetProcess,
  704. FALSE);
  705. if ((PointerPte->u.Soft.Valid == 0) &&
  706. (PointerPte->u.Soft.Prototype == 1) &&
  707. (Vad->u.VadFlags.PrivateMemory == 0) &&
  708. (Vad->ControlArea != (PCONTROL_AREA)NULL)) {
  709. //
  710. // Make sure the protoPTE is committed.
  711. //
  712. ProtoPte = MiGetProtoPteAddress(Vad,
  713. MI_VA_TO_VPN (Va));
  714. CapturedProtoPte.u.Long = 0;
  715. if (ProtoPte) {
  716. CapturedProtoPte = MiCaptureSystemPte (ProtoPte,
  717. TargetProcess);
  718. }
  719. if (CapturedProtoPte.u.Long == 0) {
  720. State = MEM_RESERVE;
  721. Protect = 0;
  722. }
  723. }
  724. }
  725. }
  726. }
  727. }
  728. if (PteIsZero) {
  729. //
  730. // There is no PDE at this address, the template from
  731. // the VAD supplies the information unless the VAD is
  732. // for an image file. For image files the individual
  733. // protection is on the prototype PTE.
  734. //
  735. //
  736. // Get the default protection information.
  737. //
  738. State = MEM_RESERVE;
  739. Protect = 0;
  740. if (Vad->u.VadFlags.PhysicalMapping == 1) {
  741. //
  742. // Must be banked memory, just return reserved.
  743. //
  744. NOTHING;
  745. }
  746. else if ((Vad->u.VadFlags.PrivateMemory == 0) &&
  747. (Vad->ControlArea != (PCONTROL_AREA)NULL)) {
  748. //
  749. // This VAD refers to a section. Even though the PTE is
  750. // zero, the actual page may be committed in the section.
  751. //
  752. *NextVaToQuery = (PVOID)((PCHAR)Va + PAGE_SIZE);
  753. ProtoPte = MiGetProtoPteAddress(Vad, MI_VA_TO_VPN (Va));
  754. CapturedProtoPte.u.Long = 0;
  755. if (ProtoPte) {
  756. CapturedProtoPte = MiCaptureSystemPte (ProtoPte,
  757. TargetProcess);
  758. }
  759. if (CapturedProtoPte.u.Long != 0) {
  760. State = MEM_COMMIT;
  761. if (Vad->u.VadFlags.ImageMap == 0) {
  762. Protect = MI_CONVERT_FROM_PTE_PROTECTION (
  763. Vad->u.VadFlags.Protection);
  764. }
  765. else {
  766. //
  767. // This is an image file, the protection is in the
  768. // prototype PTE.
  769. //
  770. Protect = MiGetPageProtection (&CapturedProtoPte,
  771. TargetProcess,
  772. TRUE);
  773. }
  774. }
  775. }
  776. else {
  777. //
  778. // Get the protection from the corresponding VAD.
  779. //
  780. if (Vad->u.VadFlags.MemCommit) {
  781. State = MEM_COMMIT;
  782. Protect = MI_CONVERT_FROM_PTE_PROTECTION (
  783. Vad->u.VadFlags.Protection);
  784. }
  785. }
  786. }
  787. *ReturnedProtect = Protect;
  788. return State;
  789. }
  790. NTSTATUS
  791. MiGetWorkingSetInfo (
  792. IN PMEMORY_WORKING_SET_INFORMATION WorkingSetInfo,
  793. IN SIZE_T Length,
  794. IN PEPROCESS Process
  795. )
  796. {
  797. PMDL Mdl;
  798. PMEMORY_WORKING_SET_INFORMATION Info;
  799. PMEMORY_WORKING_SET_BLOCK Entry;
  800. #if DBG
  801. PMEMORY_WORKING_SET_BLOCK LastEntry;
  802. #endif
  803. PMMWSLE Wsle;
  804. PMMWSLE LastWsle;
  805. WSLE_NUMBER WsSize;
  806. PMMPTE PointerPte;
  807. PMMPFN Pfn1;
  808. NTSTATUS status;
  809. LOGICAL Attached;
  810. KAPC_STATE ApcState;
  811. PETHREAD CurrentThread;
  812. //
  813. // Allocate an MDL to map the request.
  814. //
  815. Mdl = ExAllocatePoolWithTag (NonPagedPool,
  816. sizeof(MDL) + sizeof(PFN_NUMBER) +
  817. BYTES_TO_PAGES (Length) * sizeof(PFN_NUMBER),
  818. ' mM');
  819. if (Mdl == NULL) {
  820. return STATUS_INSUFFICIENT_RESOURCES;
  821. }
  822. //
  823. // Initialize the MDL for the request.
  824. //
  825. MmInitializeMdl(Mdl, WorkingSetInfo, Length);
  826. CurrentThread = PsGetCurrentThread ();
  827. try {
  828. MmProbeAndLockPages (Mdl,
  829. KeGetPreviousModeByThread (&CurrentThread->Tcb),
  830. IoWriteAccess);
  831. } except (EXCEPTION_EXECUTE_HANDLER) {
  832. ExFreePool (Mdl);
  833. return GetExceptionCode();
  834. }
  835. Info = MmGetSystemAddressForMdlSafe (Mdl, NormalPagePriority);
  836. if (Info == NULL) {
  837. MmUnlockPages (Mdl);
  838. ExFreePool (Mdl);
  839. return STATUS_INSUFFICIENT_RESOURCES;
  840. }
  841. if (PsGetCurrentProcessByThread (CurrentThread) != Process) {
  842. KeStackAttachProcess (&Process->Pcb, &ApcState);
  843. Attached = TRUE;
  844. }
  845. else {
  846. Attached = FALSE;
  847. }
  848. status = STATUS_SUCCESS;
  849. LOCK_WS (Process);
  850. if (Process->Flags & PS_PROCESS_FLAGS_VM_DELETED) {
  851. status = STATUS_PROCESS_IS_TERMINATING;
  852. }
  853. else {
  854. WsSize = Process->Vm.WorkingSetSize;
  855. ASSERT (WsSize != 0);
  856. Info->NumberOfEntries = WsSize;
  857. if (sizeof(MEMORY_WORKING_SET_INFORMATION) + (WsSize-1) * sizeof(ULONG_PTR) > Length) {
  858. status = STATUS_INFO_LENGTH_MISMATCH;
  859. }
  860. }
  861. if (!NT_SUCCESS(status)) {
  862. UNLOCK_WS (Process);
  863. if (Attached == TRUE) {
  864. KeUnstackDetachProcess (&ApcState);
  865. }
  866. MmUnlockPages (Mdl);
  867. ExFreePool (Mdl);
  868. return status;
  869. }
  870. Wsle = MmWsle;
  871. LastWsle = &MmWsle[MmWorkingSetList->LastEntry];
  872. Entry = &Info->WorkingSetInfo[0];
  873. #if DBG
  874. LastEntry = (PMEMORY_WORKING_SET_BLOCK)(
  875. (PCHAR)Info + (Length & (~(sizeof(ULONG_PTR) - 1))));
  876. #endif
  877. do {
  878. if (Wsle->u1.e1.Valid == 1) {
  879. Entry->VirtualPage = Wsle->u1.e1.VirtualPageNumber;
  880. PointerPte = MiGetPteAddress (Wsle->u1.VirtualAddress);
  881. ASSERT (PointerPte->u.Hard.Valid == 1);
  882. Pfn1 = MI_PFN_ELEMENT (PointerPte->u.Hard.PageFrameNumber);
  883. #if defined(MI_MULTINODE)
  884. Entry->Node = Pfn1->u3.e1.PageColor;
  885. #else
  886. Entry->Node = 0;
  887. #endif
  888. Entry->Shared = Pfn1->u3.e1.PrototypePte;
  889. if (Pfn1->u3.e1.PrototypePte == 0) {
  890. Entry->ShareCount = 0;
  891. Entry->Protection = MI_GET_PROTECTION_FROM_SOFT_PTE(&Pfn1->OriginalPte);
  892. }
  893. else {
  894. if (Pfn1->u2.ShareCount <= 7) {
  895. Entry->ShareCount = Pfn1->u2.ShareCount;
  896. }
  897. else {
  898. Entry->ShareCount = 7;
  899. }
  900. if (Wsle->u1.e1.SameProtectAsProto == 1) {
  901. Entry->Protection = MI_GET_PROTECTION_FROM_SOFT_PTE(&Pfn1->OriginalPte);
  902. }
  903. else {
  904. Entry->Protection = Wsle->u1.e1.Protection;
  905. }
  906. }
  907. Entry += 1;
  908. }
  909. Wsle += 1;
  910. #if DBG
  911. ASSERT ((Entry < LastEntry) || (Wsle > LastWsle));
  912. #endif
  913. } while (Wsle <= LastWsle);
  914. UNLOCK_WS (Process);
  915. if (Attached == TRUE) {
  916. KeUnstackDetachProcess (&ApcState);
  917. }
  918. MmUnlockPages (Mdl);
  919. ExFreePool (Mdl);
  920. return STATUS_SUCCESS;
  921. }