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.

661 lines
19 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. acceschk.c
  5. Abstract:
  6. This module contains the access check routines for memory management.
  7. Author:
  8. Lou Perazzoli (loup) 10-Apr-1989
  9. Landy Wang (landyw) 02-June-1997
  10. Revision History:
  11. --*/
  12. #include "mi.h"
  13. #if defined(_WIN64)
  14. #include "wow64t.h"
  15. #pragma alloc_text(PAGE, MiCheckForUserStackOverflow)
  16. #if PAGE_SIZE != PAGE_SIZE_X86NT
  17. #define EMULATE_USERMODE_STACK_4K 1
  18. #endif
  19. #endif
  20. //
  21. // MmReadWrite yields 0 if no-access, 10 if read-only, 11 if read-write.
  22. // It is indexed by a page protection. The value of this array is added
  23. // to the !WriteOperation value. If the value is 10 or less an access
  24. // violation is issued (read-only - write_operation) = 9,
  25. // (read_only - read_operation) = 10, etc.
  26. //
  27. CCHAR MmReadWrite[32] = {1, 10, 10, 10, 11, 11, 11, 11,
  28. 1, 10, 10, 10, 11, 11, 11, 11,
  29. 1, 10, 10, 10, 11, 11, 11, 11,
  30. 1, 10, 10, 10, 11, 11, 11, 11 };
  31. NTSTATUS
  32. MiAccessCheck (
  33. IN PMMPTE PointerPte,
  34. IN ULONG_PTR WriteOperation,
  35. IN KPROCESSOR_MODE PreviousMode,
  36. IN ULONG Protection,
  37. IN BOOLEAN CallerHoldsPfnLock
  38. )
  39. /*++
  40. Routine Description:
  41. Arguments:
  42. PointerPte - Supplies the pointer to the PTE which caused the
  43. page fault.
  44. WriteOperation - Supplies nonzero if the operation is a write, 0 if
  45. the operation is a read.
  46. PreviousMode - Supplies the previous mode, one of UserMode or KernelMode.
  47. Protection - Supplies the protection mask to check.
  48. CallerHoldsPfnLock - Supplies TRUE if the PFN lock is held, FALSE otherwise.
  49. Return Value:
  50. Returns TRUE if access to the page is allowed, FALSE otherwise.
  51. Environment:
  52. Kernel mode, APCs disabled.
  53. --*/
  54. {
  55. MMPTE PteContents;
  56. KIRQL OldIrql;
  57. PMMPFN Pfn1;
  58. //
  59. // Check to see if the owner bit allows access to the previous mode.
  60. // Access is not allowed if the owner is kernel and the previous
  61. // mode is user. Access is also disallowed if the write operation
  62. // is true and the write field in the PTE is false.
  63. //
  64. //
  65. // If both an access violation and a guard page violation could
  66. // occur for the page, the access violation must be returned.
  67. //
  68. if (PreviousMode == UserMode) {
  69. if (PointerPte > MiHighestUserPte) {
  70. return STATUS_ACCESS_VIOLATION;
  71. }
  72. }
  73. PteContents = *PointerPte;
  74. if (PteContents.u.Hard.Valid == 1) {
  75. //
  76. // Valid pages cannot be guard page violations.
  77. //
  78. if (WriteOperation != 0) {
  79. if ((PteContents.u.Hard.Write == 1) ||
  80. (PteContents.u.Hard.CopyOnWrite == 1)) {
  81. return STATUS_SUCCESS;
  82. }
  83. return STATUS_ACCESS_VIOLATION;
  84. }
  85. return STATUS_SUCCESS;
  86. }
  87. if (WriteOperation != 0) {
  88. WriteOperation = 1;
  89. }
  90. if ((MmReadWrite[Protection] - (CCHAR)WriteOperation) < 10) {
  91. return STATUS_ACCESS_VIOLATION;
  92. }
  93. //
  94. // Check for a guard page fault.
  95. //
  96. if (Protection & MM_GUARD_PAGE) {
  97. //
  98. // If this thread is attached to a different process,
  99. // return an access violation rather than a guard
  100. // page exception. The prevents problems with unwanted
  101. // stack expansion and unexpected guard page behavior
  102. // from debuggers.
  103. //
  104. if (KeIsAttachedProcess()) {
  105. return STATUS_ACCESS_VIOLATION;
  106. }
  107. //
  108. // Check to see if this is a transition PTE. If so, the
  109. // PFN database original contents field needs to be updated.
  110. //
  111. if ((PteContents.u.Soft.Transition == 1) &&
  112. (PteContents.u.Soft.Prototype == 0)) {
  113. //
  114. // Acquire the PFN lock and check to see if the
  115. // PTE is still in the transition state. If so,
  116. // update the original PTE in the PFN database.
  117. //
  118. //
  119. // Initializing OldIrql is not needed for correctness but
  120. // without it the compiler cannot compile this code
  121. // W4 to check for use of uninitialized variables.
  122. //
  123. OldIrql = PASSIVE_LEVEL;
  124. if (CallerHoldsPfnLock == FALSE) {
  125. LOCK_PFN (OldIrql);
  126. }
  127. PteContents = *(volatile MMPTE *)PointerPte;
  128. if ((PteContents.u.Soft.Transition == 1) &&
  129. (PteContents.u.Soft.Prototype == 0)) {
  130. //
  131. // Still in transition, update the PFN database.
  132. //
  133. Pfn1 = MI_PFN_ELEMENT (PteContents.u.Trans.PageFrameNumber);
  134. //
  135. // Note that forked processes using guard pages only take the
  136. // guard page fault when the first thread in either process
  137. // access the address. This seems to be the best behavior we
  138. // can provide users of this API as we must allow the first
  139. // thread to make forward progress and the guard attribute is
  140. // stored in the shared fork prototype PTE.
  141. //
  142. if (PteContents.u.Soft.Protection == MM_NOACCESS) {
  143. ASSERT ((Pfn1->u3.e1.PrototypePte == 1) &&
  144. (MiLocateCloneAddress (PsGetCurrentProcess (), Pfn1->PteAddress) != NULL));
  145. if (CallerHoldsPfnLock == FALSE) {
  146. UNLOCK_PFN (OldIrql);
  147. }
  148. return STATUS_ACCESS_VIOLATION;
  149. }
  150. ASSERT ((Pfn1->u3.e1.PrototypePte == 0) ||
  151. (MiLocateCloneAddress (PsGetCurrentProcess (), Pfn1->PteAddress) != NULL));
  152. Pfn1->OriginalPte.u.Soft.Protection =
  153. Protection & ~MM_GUARD_PAGE;
  154. }
  155. if (CallerHoldsPfnLock == FALSE) {
  156. UNLOCK_PFN (OldIrql);
  157. }
  158. }
  159. PointerPte->u.Soft.Protection = Protection & ~MM_GUARD_PAGE;
  160. return STATUS_GUARD_PAGE_VIOLATION;
  161. }
  162. return STATUS_SUCCESS;
  163. }
  164. NTSTATUS
  165. FASTCALL
  166. MiCheckForUserStackOverflow (
  167. IN PVOID FaultingAddress
  168. )
  169. /*++
  170. Routine Description:
  171. This routine checks to see if the faulting address is within
  172. the stack limits and if so tries to create another guard
  173. page on the stack. A stack overflow is returned if the
  174. creation of a new guard page fails or if the stack is in
  175. the following form:
  176. stack +----------------+
  177. growth | | StackBase
  178. | +----------------+
  179. v | |
  180. | allocated |
  181. | |
  182. | ... |
  183. | |
  184. +----------------+
  185. | old guard page | <- faulting address is in this page.
  186. +----------------+
  187. | |
  188. +----------------+
  189. | | last page of stack (always no access)
  190. +----------------+
  191. In this case, the page before the last page is committed, but
  192. not as a guard page and a STACK_OVERFLOW condition is returned.
  193. Arguments:
  194. FaultingAddress - Supplies the virtual address of the page which
  195. was a guard page.
  196. Return Value:
  197. NTSTATUS.
  198. Environment:
  199. Kernel mode. No mutexes held.
  200. --*/
  201. {
  202. PTEB Teb;
  203. PPEB Peb;
  204. ULONG_PTR NextPage;
  205. SIZE_T RegionSize;
  206. NTSTATUS status;
  207. PVOID DeallocationStack;
  208. PVOID *StackLimit;
  209. PETHREAD Thread;
  210. ULONG_PTR PageSize;
  211. PEPROCESS Process;
  212. ULONG OldProtection;
  213. ULONG ExecuteFlags;
  214. ULONG ProtectionFlags;
  215. LOGICAL RevertExecuteFlag;
  216. ULONG StackProtection;
  217. #if defined(_WIN64)
  218. PTEB32 Teb32;
  219. Teb32 = NULL;
  220. #endif
  221. //
  222. // Make sure we are not recursing with the address space mutex held.
  223. //
  224. Thread = PsGetCurrentThread ();
  225. if (Thread->AddressSpaceOwner == 1) {
  226. ASSERT (KeGetCurrentIrql () == APC_LEVEL);
  227. return STATUS_GUARD_PAGE_VIOLATION;
  228. }
  229. Process = NULL;
  230. Teb = Thread->Tcb.Teb;
  231. //
  232. // Create an exception handler as the TEB is within the user's
  233. // address space.
  234. //
  235. try {
  236. //
  237. // Initialize default protections early so that they can be used on
  238. // all code paths.
  239. //
  240. ProtectionFlags = PAGE_READWRITE | PAGE_GUARD;
  241. RevertExecuteFlag = FALSE;
  242. StackProtection = PAGE_READWRITE;
  243. #if defined(_IA64_)
  244. if ((Teb->NtTib.StackBase <= FaultingAddress) &&
  245. (Teb->DeallocationBStore > FaultingAddress)) {
  246. //
  247. // Check to see if the faulting address is within
  248. // the bstore limits and if so try to create another guard
  249. // page in the bstore.
  250. //
  251. //
  252. // +----------------+
  253. // | | last page of stack (always no access)
  254. // +----------------+
  255. // | |
  256. // | |
  257. // | |
  258. // +----------------+
  259. // | old guard page | <- faulting address is in this page. |
  260. // +----------------+
  261. // bstore | |
  262. // growth | ...... |
  263. // | |
  264. // ^ | allocated |
  265. // | | | StackBase
  266. // +----------------+
  267. //
  268. //
  269. NextPage = (ULONG_PTR)PAGE_ALIGN(FaultingAddress) + PAGE_SIZE;
  270. RegionSize = PAGE_SIZE;
  271. if ((NextPage + PAGE_SIZE) >= (ULONG_PTR)PAGE_ALIGN(Teb->DeallocationBStore)) {
  272. //
  273. // There is no more room for expansion, attempt to
  274. // commit the page before the last page of the
  275. // stack.
  276. //
  277. NextPage = (ULONG_PTR)PAGE_ALIGN(Teb->DeallocationBStore) - PAGE_SIZE;
  278. status = ZwAllocateVirtualMemory (NtCurrentProcess(),
  279. (PVOID *)&NextPage,
  280. 0,
  281. &RegionSize,
  282. MEM_COMMIT,
  283. PAGE_READWRITE);
  284. if ( NT_SUCCESS(status) ) {
  285. Teb->BStoreLimit = (PVOID)( (PUCHAR)NextPage);
  286. }
  287. return STATUS_STACK_OVERFLOW;
  288. }
  289. Teb->BStoreLimit = (PVOID)((PUCHAR)(NextPage));
  290. }
  291. else {
  292. #endif
  293. DeallocationStack = Teb->DeallocationStack;
  294. StackLimit = &Teb->NtTib.StackLimit;
  295. //
  296. // The stack base and the stack limit are both within the stack.
  297. //
  298. if ((Teb->NtTib.StackBase <= FaultingAddress) ||
  299. (DeallocationStack > FaultingAddress)) {
  300. #if defined(_WIN64)
  301. //
  302. // Also check for the 32-bit native stack on NT64.
  303. //
  304. Teb32 = (PTEB32) Teb->NtTib.ExceptionList;
  305. if (Teb32 != NULL) {
  306. ProbeForReadSmallStructure(Teb32, sizeof(TEB32), sizeof(ULONG));
  307. if ((ULONG_PTR)Teb32->NtTib.StackBase > (ULONG_PTR)FaultingAddress &&
  308. (ULONG_PTR)Teb32->DeallocationStack <= (ULONG_PTR)FaultingAddress) {
  309. DeallocationStack = (PVOID)ULongToPtr(Teb32->DeallocationStack);
  310. StackLimit = (PVOID *)&Teb32->NtTib.StackLimit;
  311. }
  312. else {
  313. //
  314. // Not within the stack.
  315. //
  316. return STATUS_GUARD_PAGE_VIOLATION;
  317. }
  318. }
  319. else
  320. #endif
  321. {
  322. //
  323. // Not within the stack.
  324. //
  325. return STATUS_GUARD_PAGE_VIOLATION;
  326. }
  327. }
  328. //
  329. // If the image was marked for no stack extensions we will return
  330. // stack overflow immediately.
  331. //
  332. Process = PsGetCurrentProcessByThread (Thread);
  333. Peb = Process->Peb;
  334. if (Peb->NtGlobalFlag & FLG_DISABLE_STACK_EXTENSION) {
  335. return STATUS_STACK_OVERFLOW;
  336. }
  337. //
  338. // Add execute permission if necessary. We do not need to change anything
  339. // for the old guard page because either it is the first guard page of the
  340. // current thread and it will get correct protection during user mode thread
  341. // initialization (see LdrpInitialize in base\ntdll\ldrinit.c) or it is a
  342. // guard page created by this function during stack growth and in this case
  343. // it gets correct protection. We do not do anything for a wow64 process.
  344. //
  345. #if defined(_WIN64)
  346. if (Teb32 == NULL) {
  347. #endif
  348. ExecuteFlags = Peb->ExecuteOptions;
  349. if (ExecuteFlags & (MEM_EXECUTE_OPTION_STACK | MEM_EXECUTE_OPTION_DATA)) {
  350. if (ExecuteFlags & MEM_EXECUTE_OPTION_STACK) {
  351. StackProtection = PAGE_EXECUTE_READWRITE;
  352. ProtectionFlags = PAGE_EXECUTE_READWRITE | PAGE_GUARD;
  353. }
  354. else {
  355. //
  356. // The stack must be made non-executable. The
  357. // ZwAllocateVirtualMemory call below will make it executable
  358. // because this process is marked as wanting executable data
  359. // and ZwAllocate cannot tell this is really a stack
  360. // allocation.
  361. //
  362. ASSERT (ExecuteFlags & MEM_EXECUTE_OPTION_DATA);
  363. RevertExecuteFlag = TRUE;
  364. }
  365. }
  366. #if defined(_WIN64)
  367. }
  368. #endif
  369. //
  370. // This address is within the current stack, check to see
  371. // if there is ample room for another guard page and
  372. // if so attempt to commit a new guard page.
  373. //
  374. #if EMULATE_USERMODE_STACK_4K
  375. if (Teb32 != NULL)
  376. {
  377. NextPage = ((ULONG_PTR)PAGE_4K_ALIGN(FaultingAddress) - PAGE_4K);
  378. DeallocationStack = PAGE_4K_ALIGN(DeallocationStack);
  379. PageSize = RegionSize = PAGE_4K;
  380. //
  381. // Don't set the 'G' bit on the native PTE. Let's just set
  382. // 'G' bit on the AltPte.
  383. //
  384. ProtectionFlags &= ~PAGE_GUARD;
  385. }
  386. else
  387. #endif
  388. {
  389. NextPage = ((ULONG_PTR)PAGE_ALIGN(FaultingAddress) - PAGE_SIZE);
  390. DeallocationStack = PAGE_ALIGN(DeallocationStack);
  391. PageSize = RegionSize = PAGE_SIZE;
  392. }
  393. if ((NextPage - PageSize) <= (ULONG_PTR)DeallocationStack) {
  394. //
  395. // There is no more room for expansion, attempt to
  396. // commit the page before the last page of the
  397. // stack.
  398. //
  399. NextPage = (ULONG_PTR)DeallocationStack + PageSize;
  400. status = ZwAllocateVirtualMemory (NtCurrentProcess(),
  401. (PVOID *)&NextPage,
  402. 0,
  403. &RegionSize,
  404. MEM_COMMIT,
  405. StackProtection);
  406. if (NT_SUCCESS(status)) {
  407. #if defined(_WIN64)
  408. if (Teb32) {
  409. // update the 32-bit stacklimit
  410. *(ULONG *)StackLimit = PtrToUlong((PUCHAR)NextPage);
  411. }
  412. else {
  413. *StackLimit = (PVOID)( (PUCHAR)NextPage);
  414. }
  415. #else
  416. *StackLimit = (PVOID)( (PUCHAR)NextPage);
  417. #endif
  418. //
  419. // Revert the EXECUTE bit with an extra protect() call
  420. // if we get it by default but it is not desired.
  421. //
  422. if (RevertExecuteFlag) {
  423. status = ZwProtectVirtualMemory (NtCurrentProcess(),
  424. (PVOID *)&NextPage,
  425. &RegionSize,
  426. StackProtection,
  427. &OldProtection);
  428. ASSERT (StackProtection & PAGE_READWRITE);
  429. }
  430. }
  431. return STATUS_STACK_OVERFLOW;
  432. }
  433. #if defined(_WIN64)
  434. if (Teb32 != NULL) {
  435. //
  436. // Update the 32-bit stack limit.
  437. //
  438. *(ULONG *)StackLimit = PtrToUlong((PUCHAR)(NextPage + PageSize));
  439. }
  440. else {
  441. *StackLimit = (PVOID)((PUCHAR)(NextPage + PAGE_SIZE));
  442. }
  443. #else
  444. *StackLimit = (PVOID)((PUCHAR)(NextPage + PAGE_SIZE));
  445. #endif
  446. #if defined(_IA64_)
  447. }
  448. #endif // _IA64_
  449. //
  450. // Set the guard page. For wow64 processes the protection
  451. // will not contain the PAGE_GUARD bit. This is ok since in these
  452. // cases we will set the bit for the top emulated 4K page.
  453. //
  454. status = ZwAllocateVirtualMemory (NtCurrentProcess(),
  455. (PVOID *)&NextPage,
  456. 0,
  457. &RegionSize,
  458. MEM_COMMIT,
  459. ProtectionFlags);
  460. if (NT_SUCCESS(status) || (status == STATUS_ALREADY_COMMITTED)) {
  461. //
  462. // Revert the EXECUTE bit with an extra protect() call
  463. // if we get it by default but it is not desired.
  464. //
  465. if (RevertExecuteFlag) {
  466. if (ProtectionFlags & PAGE_GUARD) {
  467. ProtectionFlags = PAGE_READWRITE | PAGE_GUARD;
  468. }
  469. else {
  470. ProtectionFlags = PAGE_READWRITE;
  471. }
  472. status = ZwProtectVirtualMemory (NtCurrentProcess(),
  473. (PVOID *)&NextPage,
  474. &RegionSize,
  475. ProtectionFlags,
  476. &OldProtection);
  477. }
  478. #if EMULATE_USERMODE_STACK_4K
  479. if (Teb32 != NULL) {
  480. LOCK_ADDRESS_SPACE (Process);
  481. MiProtectFor4kPage ((PVOID)NextPage,
  482. RegionSize,
  483. (MM_READWRITE | MM_GUARD_PAGE),
  484. ALT_CHANGE,
  485. Process);
  486. UNLOCK_ADDRESS_SPACE (Process);
  487. }
  488. #endif
  489. //
  490. // The guard page is now committed or stack space is
  491. // already present, return success.
  492. //
  493. return STATUS_PAGE_FAULT_GUARD_PAGE;
  494. }
  495. return STATUS_STACK_OVERFLOW;
  496. } except (EXCEPTION_EXECUTE_HANDLER) {
  497. //
  498. // An exception has occurred during the referencing of the
  499. // TEB or TIB, just return a guard page violation and
  500. // don't deal with the stack overflow.
  501. //
  502. return STATUS_GUARD_PAGE_VIOLATION;
  503. }
  504. }