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.

699 lines
20 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. This 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. SATISFY_OVERZEALOUS_COMPILER (OldIrql = PASSIVE_LEVEL);
  119. if (CallerHoldsPfnLock == FALSE) {
  120. LOCK_PFN (OldIrql);
  121. }
  122. PteContents = *PointerPte;
  123. if ((PteContents.u.Soft.Transition == 1) &&
  124. (PteContents.u.Soft.Prototype == 0)) {
  125. //
  126. // Still in transition, update the PFN database.
  127. //
  128. Pfn1 = MI_PFN_ELEMENT (PteContents.u.Trans.PageFrameNumber);
  129. //
  130. // Note that forked processes using guard pages only take the
  131. // guard page fault when the first thread in either process
  132. // access the address. This seems to be the best behavior we
  133. // can provide users of this API as we must allow the first
  134. // thread to make forward progress and the guard attribute is
  135. // stored in the shared fork prototype PTE.
  136. //
  137. if (PteContents.u.Soft.Protection == MM_NOACCESS) {
  138. ASSERT ((Pfn1->u3.e1.PrototypePte == 1) &&
  139. (MiLocateCloneAddress (PsGetCurrentProcess (), Pfn1->PteAddress) != NULL));
  140. if (CallerHoldsPfnLock == FALSE) {
  141. UNLOCK_PFN (OldIrql);
  142. }
  143. return STATUS_ACCESS_VIOLATION;
  144. }
  145. ASSERT ((Pfn1->u3.e1.PrototypePte == 0) ||
  146. (MiLocateCloneAddress (PsGetCurrentProcess (), Pfn1->PteAddress) != NULL));
  147. Pfn1->OriginalPte.u.Soft.Protection =
  148. Protection & ~MM_GUARD_PAGE;
  149. }
  150. if (CallerHoldsPfnLock == FALSE) {
  151. UNLOCK_PFN (OldIrql);
  152. }
  153. }
  154. PointerPte->u.Soft.Protection = Protection & ~MM_GUARD_PAGE;
  155. return STATUS_GUARD_PAGE_VIOLATION;
  156. }
  157. return STATUS_SUCCESS;
  158. }
  159. NTSTATUS
  160. FASTCALL
  161. MiCheckForUserStackOverflow (
  162. IN PVOID FaultingAddress
  163. )
  164. /*++
  165. Routine Description:
  166. This routine checks to see if the faulting address is within
  167. the stack limits and if so tries to create another guard
  168. page on the stack. A stack overflow is returned if the
  169. creation of a new guard page fails or if the stack is in
  170. the following form:
  171. stack +----------------+
  172. growth | | StackBase
  173. | +----------------+
  174. v | |
  175. | allocated |
  176. | |
  177. | ... |
  178. | |
  179. +----------------+
  180. | old guard page | <- faulting address is in this page.
  181. +----------------+
  182. | |
  183. +----------------+
  184. | | last page of stack (always no access)
  185. +----------------+
  186. In this case, the page before the last page is committed, but
  187. not as a guard page and a STACK_OVERFLOW condition is returned.
  188. Arguments:
  189. FaultingAddress - Supplies the virtual address of the page which
  190. was a guard page.
  191. Return Value:
  192. NTSTATUS.
  193. Environment:
  194. Kernel mode. No mutexes held.
  195. --*/
  196. {
  197. PTEB Teb;
  198. PPEB Peb;
  199. ULONG_PTR NextPage;
  200. SIZE_T RegionSize;
  201. NTSTATUS status;
  202. PVOID DeallocationStack;
  203. PVOID *StackLimit;
  204. PVOID StackBase;
  205. PETHREAD Thread;
  206. ULONG_PTR PageSize;
  207. PEPROCESS Process;
  208. ULONG OldProtection;
  209. ULONG ExecuteFlags;
  210. ULONG ProtectionFlags;
  211. LOGICAL RevertExecuteFlag;
  212. ULONG StackProtection;
  213. #if defined (_IA64_)
  214. PVOID DeallocationBStore;
  215. #endif
  216. #if defined(_WIN64)
  217. PTEB32 Teb32;
  218. Teb32 = NULL;
  219. #endif
  220. //
  221. // Make sure we are not recursing with the address space mutex held.
  222. //
  223. Thread = PsGetCurrentThread ();
  224. if (Thread->AddressSpaceOwner == 1) {
  225. ASSERT (KeAreAllApcsDisabled () == TRUE);
  226. return STATUS_GUARD_PAGE_VIOLATION;
  227. }
  228. //
  229. // If this thread is attached to a different process,
  230. // return an access violation rather than a guard
  231. // page exception. This prevents problems with unwanted
  232. // stack expansion and unexpected guard page behavior
  233. // from debuggers.
  234. //
  235. // Note we must bail when attached because we reference fields in our
  236. // TEB below which have no relationship to the virtual address space
  237. // of the process we are attached to. Rather than introduce random
  238. // behavior into applications, it's better to consistently return an AV
  239. // for this scenario (one thread trying to grow another's stack).
  240. //
  241. if (KeIsAttachedProcess()) {
  242. return STATUS_GUARD_PAGE_VIOLATION;
  243. }
  244. Process = NULL;
  245. //
  246. // Initialize default protections early so that they can be used on
  247. // all code paths.
  248. //
  249. ProtectionFlags = PAGE_READWRITE | PAGE_GUARD;
  250. RevertExecuteFlag = FALSE;
  251. StackProtection = PAGE_READWRITE;
  252. Teb = Thread->Tcb.Teb;
  253. //
  254. // Create an exception handler as the TEB is within the user's
  255. // address space.
  256. //
  257. try {
  258. StackBase = Teb->NtTib.StackBase;
  259. #if defined (_IA64_)
  260. DeallocationBStore = Teb->DeallocationBStore;
  261. if ((FaultingAddress >= StackBase) &&
  262. (FaultingAddress < DeallocationBStore)) {
  263. //
  264. // Check to see if the faulting address is within
  265. // the bstore limits and if so try to create another guard
  266. // page in the bstore.
  267. //
  268. //
  269. // +----------------+
  270. // | | last page of stack (always no access)
  271. // +----------------+
  272. // | |
  273. // | |
  274. // | |
  275. // +----------------+
  276. // | old guard page | <- faulting address is in this page.
  277. // +----------------+
  278. // bstore | |
  279. // growth | ...... |
  280. // | |
  281. // ^ | allocated |
  282. // | | | StackBase
  283. // +----------------+
  284. //
  285. //
  286. NextPage = (ULONG_PTR)PAGE_ALIGN(FaultingAddress) + PAGE_SIZE;
  287. RegionSize = PAGE_SIZE;
  288. if ((NextPage + PAGE_SIZE) >= (ULONG_PTR)PAGE_ALIGN(DeallocationBStore)) {
  289. //
  290. // There is no more room for expansion, attempt to
  291. // commit the page before the last page of the stack.
  292. //
  293. NextPage = (ULONG_PTR)PAGE_ALIGN(DeallocationBStore) - PAGE_SIZE;
  294. status = ZwAllocateVirtualMemory (NtCurrentProcess(),
  295. (PVOID *)&NextPage,
  296. 0,
  297. &RegionSize,
  298. MEM_COMMIT,
  299. PAGE_READWRITE);
  300. if (NT_SUCCESS(status)) {
  301. Teb->BStoreLimit = (PVOID) NextPage;
  302. }
  303. return STATUS_STACK_OVERFLOW;
  304. }
  305. Teb->BStoreLimit = (PVOID) NextPage;
  306. goto AllocateTheGuard;
  307. }
  308. #endif
  309. DeallocationStack = Teb->DeallocationStack;
  310. StackLimit = &Teb->NtTib.StackLimit;
  311. //
  312. // The faulting address must be below the stack base and
  313. // above the stack limit.
  314. //
  315. if ((FaultingAddress >= StackBase) ||
  316. (FaultingAddress < DeallocationStack)) {
  317. //
  318. // Not within the native stack.
  319. //
  320. #if defined (_WIN64)
  321. //
  322. // Also check for the 32-bit stack if this is a Wow64 process.
  323. //
  324. Process = PsGetCurrentProcessByThread (Thread);
  325. if (Process->Wow64Process != NULL) {
  326. Teb32 = (PTEB32) Teb->NtTib.ExceptionList;
  327. if (Teb32 != NULL) {
  328. ProbeForReadSmallStructure (Teb32,
  329. sizeof(TEB32),
  330. sizeof(ULONG));
  331. StackBase = (PVOID) (ULONG_PTR) Teb32->NtTib.StackBase;
  332. DeallocationStack = (PVOID) (ULONG_PTR) Teb32->DeallocationStack;
  333. if ((FaultingAddress >= StackBase) ||
  334. (FaultingAddress < DeallocationStack)) {
  335. //
  336. // Not within the stack.
  337. //
  338. return STATUS_GUARD_PAGE_VIOLATION;
  339. }
  340. StackLimit = (PVOID *)&Teb32->NtTib.StackLimit;
  341. goto ExtendTheStack;
  342. }
  343. }
  344. #endif
  345. //
  346. // Not within the stack.
  347. //
  348. return STATUS_GUARD_PAGE_VIOLATION;
  349. }
  350. #if defined (_WIN64)
  351. ExtendTheStack:
  352. #endif
  353. //
  354. // If the image was marked for no stack extensions,
  355. // return stack overflow immediately.
  356. //
  357. Process = PsGetCurrentProcessByThread (Thread);
  358. Peb = Process->Peb;
  359. if (Peb->NtGlobalFlag & FLG_DISABLE_STACK_EXTENSION) {
  360. return STATUS_STACK_OVERFLOW;
  361. }
  362. //
  363. // Add execute permission if necessary. We do not need to change
  364. // anything for the old guard page because either it is the first
  365. // guard page of the current thread and it will get correct
  366. // protection during user mode thread initialization (see
  367. // LdrpInitialize in base\ntdll\ldrinit.c) or it is a
  368. // guard page created by this function during stack growth
  369. // and in this case it gets correct protection.
  370. //
  371. #if defined(_WIN64)
  372. if (Teb32 != NULL) {
  373. ASSERT (Process->Wow64Process != NULL);
  374. ExecuteFlags = ((PPEB32)(Process->Wow64Process->Wow64))->ExecuteOptions;
  375. } else {
  376. #endif
  377. ExecuteFlags = Peb->ExecuteOptions;
  378. #if defined(_WIN64)
  379. }
  380. #endif
  381. if (ExecuteFlags & (MEM_EXECUTE_OPTION_STACK | MEM_EXECUTE_OPTION_DATA)) {
  382. if (ExecuteFlags & MEM_EXECUTE_OPTION_STACK) {
  383. StackProtection = PAGE_EXECUTE_READWRITE;
  384. ProtectionFlags = PAGE_EXECUTE_READWRITE | PAGE_GUARD;
  385. }
  386. else {
  387. //
  388. // The stack must be made non-executable. The
  389. // ZwAllocateVirtualMemory call below will make it
  390. // executable because this process is marked as wanting
  391. // executable data and ZwAllocate cannot tell this is
  392. // really a stack allocation.
  393. //
  394. ASSERT (ExecuteFlags & MEM_EXECUTE_OPTION_DATA);
  395. RevertExecuteFlag = TRUE;
  396. }
  397. }
  398. //
  399. // This address is within the current stack, if there is ample
  400. // room for another guard page then attempt to commit it.
  401. //
  402. #if EMULATE_USERMODE_STACK_4K
  403. if (Teb32 != NULL) {
  404. NextPage = (ULONG_PTR) PAGE_4K_ALIGN (FaultingAddress) - PAGE_4K;
  405. DeallocationStack = PAGE_4K_ALIGN (DeallocationStack);
  406. PageSize = PAGE_4K;
  407. RegionSize = PAGE_4K;
  408. //
  409. // Don't set the guard bit in the native PTE - just set
  410. // it in the AltPte.
  411. //
  412. ProtectionFlags &= ~PAGE_GUARD;
  413. }
  414. else
  415. #endif
  416. {
  417. NextPage = (ULONG_PTR)PAGE_ALIGN (FaultingAddress) - PAGE_SIZE;
  418. DeallocationStack = PAGE_ALIGN (DeallocationStack);
  419. PageSize = PAGE_SIZE;
  420. RegionSize = PAGE_SIZE;
  421. }
  422. if ((NextPage - PageSize) <= (ULONG_PTR)DeallocationStack) {
  423. //
  424. // There is no more room for expansion, attempt to
  425. // commit the page before the last page of the stack.
  426. //
  427. NextPage = (ULONG_PTR)DeallocationStack + PageSize;
  428. status = ZwAllocateVirtualMemory (NtCurrentProcess(),
  429. (PVOID *)&NextPage,
  430. 0,
  431. &RegionSize,
  432. MEM_COMMIT,
  433. StackProtection);
  434. if (NT_SUCCESS(status)) {
  435. #if defined(_WIN64)
  436. if (Teb32 != NULL) {
  437. *(PULONG) StackLimit = (ULONG) NextPage;
  438. }
  439. else
  440. #endif
  441. *StackLimit = (PVOID) NextPage;
  442. //
  443. // Revert the EXECUTE bit if we get it by default
  444. // but it is not desired.
  445. //
  446. if (RevertExecuteFlag) {
  447. status = ZwProtectVirtualMemory (NtCurrentProcess(),
  448. (PVOID *)&NextPage,
  449. &RegionSize,
  450. StackProtection,
  451. &OldProtection);
  452. ASSERT (StackProtection & PAGE_READWRITE);
  453. }
  454. }
  455. return STATUS_STACK_OVERFLOW;
  456. }
  457. #if defined(_WIN64)
  458. if (Teb32 != NULL) {
  459. //
  460. // Update the 32-bit stack limit.
  461. //
  462. *(PULONG) StackLimit = (ULONG) (NextPage + PageSize);
  463. }
  464. else
  465. #endif
  466. *StackLimit = (PVOID)(NextPage + PAGE_SIZE);
  467. } except (EXCEPTION_EXECUTE_HANDLER) {
  468. //
  469. // An exception has occurred during the referencing of the
  470. // TEB or TIB, just return a guard page violation and
  471. // don't deal with the stack overflow.
  472. //
  473. return STATUS_GUARD_PAGE_VIOLATION;
  474. }
  475. #if defined (_IA64_)
  476. AllocateTheGuard:
  477. #endif
  478. //
  479. // Set the guard page. For Wow64 processes the protection
  480. // will not contain the PAGE_GUARD bit. This is ok since in these
  481. // cases we will set the bit for the top emulated 4K page.
  482. //
  483. status = ZwAllocateVirtualMemory (NtCurrentProcess(),
  484. (PVOID *)&NextPage,
  485. 0,
  486. &RegionSize,
  487. MEM_COMMIT,
  488. ProtectionFlags);
  489. if (NT_SUCCESS(status) || (status == STATUS_ALREADY_COMMITTED)) {
  490. //
  491. // Revert the EXECUTE bit with an extra protect() call
  492. // if we get it by default but it is not desired.
  493. //
  494. if (RevertExecuteFlag) {
  495. if (ProtectionFlags & PAGE_GUARD) {
  496. ProtectionFlags = PAGE_READWRITE | PAGE_GUARD;
  497. }
  498. else {
  499. ProtectionFlags = PAGE_READWRITE;
  500. }
  501. status = ZwProtectVirtualMemory (NtCurrentProcess(),
  502. (PVOID *)&NextPage,
  503. &RegionSize,
  504. ProtectionFlags,
  505. &OldProtection);
  506. }
  507. #if EMULATE_USERMODE_STACK_4K
  508. if (Teb32 != NULL) {
  509. LOCK_ADDRESS_SPACE (Process);
  510. MiProtectFor4kPage ((PVOID)NextPage,
  511. RegionSize,
  512. (MM_READWRITE | MM_GUARD_PAGE),
  513. ALT_CHANGE,
  514. Process);
  515. UNLOCK_ADDRESS_SPACE (Process);
  516. }
  517. #endif
  518. //
  519. // The guard page is now committed or stack space is
  520. // already present, return success.
  521. //
  522. return STATUS_PAGE_FAULT_GUARD_PAGE;
  523. }
  524. return STATUS_STACK_OVERFLOW;
  525. }