Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

acceschk.c

Go to the documentation of this file.
00001 /*++ 00002 00003 Copyright (c) 1989 Microsoft Corporation 00004 00005 Module Name: 00006 00007 acceschk.c 00008 00009 Abstract: 00010 00011 This module contains the access check routines for memory management. 00012 00013 Author: 00014 00015 Lou Perazzoli (loup) 10-Apr-1989 00016 00017 Revision History: 00018 00019 --*/ 00020 00021 #include "mi.h" 00022 00023 #if defined(_WIN64) 00024 #include "wow64t.h" 00025 #endif 00026 00027 // 00028 // MmReadWrite yields 0 if no-access, 10 if read-only, 11 if read-write. 00029 // It is indexed by a page protection. The value of this array is added 00030 // to the !WriteOperation value. If the value is 10 or less an access 00031 // violation is issued (read-only - write_operation) = 9, 00032 // (read_only - read_operation) = 10, etc. 00033 // 00034 00035 CCHAR MmReadWrite[32] = {1, 10, 10, 10, 11, 11, 11, 11, 00036 1, 10, 10, 10, 11, 11, 11, 11, 00037 1, 10, 10, 10, 11, 11, 11, 11, 00038 1, 10, 10, 10, 11, 11, 11, 11 }; 00039 00040 00041 NTSTATUS 00042 MiAccessCheck ( 00043 IN PMMPTE PointerPte, 00044 IN BOOLEAN WriteOperation, 00045 IN KPROCESSOR_MODE PreviousMode, 00046 IN ULONG Protection, 00047 IN BOOLEAN CallerHoldsPfnLock 00048 ) 00049 00050 /*++ 00051 00052 Routine Description: 00053 00054 00055 00056 Arguments: 00057 00058 PointerPte - Supplies the pointer to the PTE which caused the 00059 page fault. 00060 00061 WriteOperation - Supplies 1 if the operation is a write, 0 if 00062 the operation is a read. 00063 00064 PreviousMode - Supplies the previous mode, one of UserMode or KernelMode. 00065 00066 Protection - Supplies the protection mask to check. 00067 00068 CallerHoldsPfnLock - Supplies TRUE if the PFN lock is held, FALSE otherwise. 00069 00070 Return Value: 00071 00072 Returns TRUE if access to the page is allowed, FALSE otherwise. 00073 00074 Environment: 00075 00076 Kernel mode, APCs disabled. 00077 00078 --*/ 00079 00080 { 00081 MMPTE PteContents; 00082 KIRQL OldIrql; 00083 PMMPFN Pfn1; 00084 00085 // 00086 // Check to see if the owner bit allows access to the previous mode. 00087 // Access is not allowed if the owner is kernel and the previous 00088 // mode is user. Access is also disallowed if the write operation 00089 // is true and the write field in the PTE is false. 00090 // 00091 00092 // 00093 // If both an access violation and a guard page violation could 00094 // occur for the page, the access violation must be returned. 00095 // 00096 00097 if (PreviousMode == UserMode) { 00098 if (PointerPte > MiHighestUserPte) { 00099 return STATUS_ACCESS_VIOLATION; 00100 } 00101 } 00102 00103 PteContents = *PointerPte; 00104 00105 if (PteContents.u.Hard.Valid == 1) { 00106 00107 // 00108 // Valid pages cannot be guard page violations. 00109 // 00110 00111 if (WriteOperation) { 00112 if ((PteContents.u.Hard.Write == 1) || 00113 (PteContents.u.Hard.CopyOnWrite == 1)) { 00114 return STATUS_SUCCESS; 00115 } else { 00116 return STATUS_ACCESS_VIOLATION; 00117 } 00118 } else { 00119 return STATUS_SUCCESS; 00120 } 00121 00122 } else { 00123 00124 if ((MmReadWrite[Protection] - (CCHAR)WriteOperation) < 10) { 00125 return STATUS_ACCESS_VIOLATION; 00126 } else { 00127 00128 // 00129 // Check for a guard page fault. 00130 // 00131 00132 if (Protection & MM_GUARD_PAGE) { 00133 00134 // 00135 // If this thread is attached to a different process, 00136 // return an access violation rather than a guard 00137 // page exception. The prevents problems with unwanted 00138 // stack expansion and unexpected guard page behavior 00139 // from debuggers. 00140 00141 if (KeIsAttachedProcess()) { 00142 return STATUS_ACCESS_VIOLATION; 00143 } 00144 00145 // 00146 // Check to see if this is a transition PTE, if so, 00147 // the PFN database original contents field needs to be 00148 // updated. 00149 // 00150 00151 if ((PteContents.u.Soft.Transition == 1) && 00152 (PteContents.u.Soft.Prototype == 0)) { 00153 00154 // 00155 // Acquire the PFN mutex and check to see if the 00156 // PTE is still in the transition state, and, if so 00157 // update the original PTE in the pfn database. 00158 // 00159 00160 if (CallerHoldsPfnLock == FALSE) { 00161 LOCK_PFN (OldIrql); 00162 } 00163 PteContents = *(volatile MMPTE *)PointerPte; 00164 if ((PteContents.u.Soft.Transition == 1) && 00165 (PteContents.u.Soft.Prototype == 0)) { 00166 00167 // 00168 // Still in transition, update the PFN database. 00169 // 00170 00171 Pfn1 = MI_PFN_ELEMENT ( 00172 PteContents.u.Trans.PageFrameNumber); 00173 00174 ASSERT (Pfn1->u3.e1.PrototypePte == 0); 00175 Pfn1->OriginalPte.u.Soft.Protection = 00176 Protection & ~MM_GUARD_PAGE; 00177 } 00178 if (CallerHoldsPfnLock == FALSE) { 00179 UNLOCK_PFN (OldIrql); 00180 } 00181 } 00182 00183 PointerPte->u.Soft.Protection = Protection & ~MM_GUARD_PAGE; 00184 00185 return STATUS_GUARD_PAGE_VIOLATION; 00186 } 00187 return STATUS_SUCCESS; 00188 } 00189 } 00190 } 00191 00192 NTSTATUS 00193 FASTCALL 00194 MiCheckForUserStackOverflow ( 00195 IN PVOID FaultingAddress 00196 ) 00197 00198 /*++ 00199 00200 Routine Description: 00201 00202 This routine checks to see if the faulting address is within 00203 the stack limits and if so tries to create another guard 00204 page on the stack. A stack over flow is returned if the 00205 creation of a new guard page fails or if the stack is in 00206 the following form: 00207 00208 00209 stack +----------------+ 00210 growth | | StackBase 00211 | +----------------+ 00212 v | | 00213 | allocated | 00214 | | 00215 | ... | 00216 | | 00217 +----------------+ 00218 | old guard page | <- faulting address is in this page. 00219 +----------------+ 00220 | | 00221 +----------------+ 00222 | | last page of stack (always no access) 00223 +----------------+ 00224 00225 In this case, the page before the last page is committed, but 00226 not as a guard page and a STACK_OVERFLOW condition is returned. 00227 00228 Arguments: 00229 00230 FaultingAddress - Supplies the virtual address of the page which 00231 was a guard page. 00232 00233 Return Value: 00234 00235 None. 00236 00237 Environment: 00238 00239 Kernel mode. No mutexes held. 00240 00241 --*/ 00242 00243 { 00244 PTEB Teb; 00245 ULONG_PTR NextPage; 00246 SIZE_T RegionSize; 00247 NTSTATUS status; 00248 KIRQL OldIrql; 00249 PMMLOCK_CONFLICT Next; 00250 PVOID DeallocationStack; 00251 PVOID *StackLimit; 00252 00253 #if defined(WX86) || defined(_AXP64_) 00254 PWX86TIB Wx86Tib; 00255 #endif 00256 #if defined(_WIN64) 00257 PTEB32 Teb32; 00258 #endif 00259 00260 // 00261 // Make sure we are not recursing with the address space or 00262 // working set lock held. 00263 // 00264 00265 if (!IsListEmpty (&MmLockConflictList)) { 00266 ExAcquireSpinLock (&MmChargeCommitmentLock, &OldIrql); 00267 Next = (PMMLOCK_CONFLICT)MmLockConflictList.Flink; 00268 00269 while ((PVOID)Next != &MmLockConflictList) { 00270 00271 if (Next->Thread == PsGetCurrentThread()) { 00272 ExReleaseSpinLock (&MmChargeCommitmentLock, OldIrql); 00273 return STATUS_GUARD_PAGE_VIOLATION; 00274 } 00275 Next = (PMMLOCK_CONFLICT)Next->List.Flink; 00276 } 00277 ExReleaseSpinLock (&MmChargeCommitmentLock, OldIrql); 00278 } 00279 00280 // 00281 // Create an exception handler as the TEB is within the user's 00282 // address space. 00283 // 00284 00285 try { 00286 00287 Teb = NtCurrentTeb(); 00288 00289 #if defined(_IA64_) 00290 00291 if ((Teb->NtTib.StackBase <= FaultingAddress) && 00292 (Teb->DeallocationBStore > FaultingAddress)) { 00293 00294 // 00295 // check to see if the faulting address is within 00296 // the bstore limits and if so tries to create another guard 00297 // page on the bstore. 00298 // 00299 // 00300 // +----------------+ 00301 // | | last page of stack (always no access) 00302 // +----------------+ 00303 // | | 00304 // | | 00305 // | | 00306 // +----------------+ 00307 // | old guard page | <- faulting address is in this page. | 00308 // +----------------+ 00309 // bstore | | 00310 // growth | ...... | 00311 // | | 00312 // ^ | allocated | 00313 // | | | StackBase 00314 // +----------------+ 00315 // 00316 // 00317 00318 NextPage = (ULONG_PTR)PAGE_ALIGN(FaultingAddress) + PAGE_SIZE; 00319 00320 RegionSize = PAGE_SIZE; 00321 00322 if ((NextPage + PAGE_SIZE) >= (ULONG_PTR)PAGE_ALIGN(Teb->DeallocationBStore)) { 00323 00324 // 00325 // There is no more room for expansion, attempt to 00326 // commit the page before the last page of the 00327 // stack. 00328 // 00329 00330 NextPage = (ULONG_PTR)PAGE_ALIGN(Teb->DeallocationBStore) - PAGE_SIZE; 00331 00332 status = ZwAllocateVirtualMemory (NtCurrentProcess(), 00333 (PVOID *)&NextPage, 00334 0, 00335 &RegionSize, 00336 MEM_COMMIT, 00337 PAGE_READWRITE); 00338 if ( NT_SUCCESS(status) ) { 00339 Teb->BStoreLimit = (PVOID)( (PUCHAR)NextPage); 00340 } 00341 00342 return STATUS_STACK_OVERFLOW; 00343 } 00344 00345 Teb->BStoreLimit = (PVOID)((PUCHAR)(NextPage)); 00346 00347 } else { 00348 00349 #endif 00350 00351 DeallocationStack = Teb->DeallocationStack; 00352 StackLimit = &Teb->NtTib.StackLimit; 00353 00354 // 00355 // The stack base and the stack limit are both within the stack. 00356 // 00357 00358 if ((Teb->NtTib.StackBase <= FaultingAddress) || 00359 (DeallocationStack > FaultingAddress)) { 00360 00361 #if defined(WX86) 00362 // 00363 // Also check the Wx86 i386 stack on risc. 00364 // 00365 Wx86Tib = Teb->Vdm; 00366 if (Wx86Tib) { 00367 ProbeForRead(Wx86Tib, sizeof(WX86TIB), sizeof(ULONG)); 00368 if (Wx86Tib->Size == sizeof(WX86TIB) && 00369 Wx86Tib->StackBase > FaultingAddress && 00370 Wx86Tib->DeallocationStack <= FaultingAddress) { 00371 00372 DeallocationStack = Wx86Tib->DeallocationStack; 00373 StackLimit = &Wx86Tib->StackLimit; 00374 } else { 00375 // 00376 // Not within the stack. 00377 // 00378 00379 return STATUS_GUARD_PAGE_VIOLATION; 00380 } 00381 } else 00382 #endif 00383 #if defined(_WIN64) 00384 // 00385 // Also check for the 32-bit native stack on NT64 00386 // 00387 if ((Teb32 = (PTEB32)Teb->NtTib.ExceptionList) != NULL) { 00388 ProbeForRead(Teb32, sizeof(TEB32), sizeof(ULONG)); 00389 if ((ULONG_PTR)Teb32->NtTib.StackBase > (ULONG_PTR)FaultingAddress && 00390 (ULONG_PTR)Teb32->DeallocationStack <= (ULONG_PTR)FaultingAddress) { 00391 DeallocationStack = (PVOID)ULongToPtr(Teb32->DeallocationStack); 00392 00393 StackLimit = (PVOID *)&Teb32->NtTib.StackLimit; 00394 } else 00395 #if defined(_AXP64_) 00396 // 00397 // Also check the Wx86 i386 stack on risc. 00398 // 00399 if (Wx86Tib = (PWX86TIB)ULongToPtr(Teb32->Vdm)) { 00400 ProbeForRead(Wx86Tib, sizeof(WX86TIB), sizeof(ULONG)); 00401 if (Wx86Tib->Size == sizeof(WX86TIB) && 00402 (ULONG_PTR)Wx86Tib->StackBase > (ULONG_PTR)FaultingAddress && 00403 (ULONG_PTR)Wx86Tib->DeallocationStack <= (ULONG_PTR)FaultingAddress) { 00404 00405 DeallocationStack = Wx86Tib->DeallocationStack; 00406 StackLimit = (PVOID *)(&Wx86Tib->StackLimit); 00407 } else { 00408 // 00409 // Not within the stack. 00410 // 00411 00412 return STATUS_GUARD_PAGE_VIOLATION; 00413 } 00414 } else 00415 #endif 00416 { 00417 // 00418 // Not within the stack. 00419 // 00420 00421 return STATUS_GUARD_PAGE_VIOLATION; 00422 } 00423 } else 00424 #endif 00425 { 00426 // 00427 // Not within the stack. 00428 // 00429 00430 return STATUS_GUARD_PAGE_VIOLATION; 00431 } 00432 } 00433 00434 // 00435 // This address is within the current stack, check to see 00436 // if there is ample room for another guard page and 00437 // if so attempt to commit a new guard page. 00438 // 00439 00440 NextPage = ((ULONG_PTR)PAGE_ALIGN(FaultingAddress) - PAGE_SIZE); 00441 00442 RegionSize = PAGE_SIZE; 00443 00444 if ((NextPage - PAGE_SIZE) <= (ULONG_PTR)PAGE_ALIGN(DeallocationStack)) { 00445 00446 // 00447 // There is no more room for expansion, attempt to 00448 // commit the page before the last page of the 00449 // stack. 00450 // 00451 00452 NextPage = (ULONG_PTR)PAGE_ALIGN(DeallocationStack) + PAGE_SIZE; 00453 00454 status = ZwAllocateVirtualMemory (NtCurrentProcess(), 00455 (PVOID *)&NextPage, 00456 0, 00457 &RegionSize, 00458 MEM_COMMIT, 00459 PAGE_READWRITE); 00460 if ( NT_SUCCESS(status) ) { 00461 00462 #if defined(_WIN64) 00463 if (Teb32) { 00464 // update the 32-bit stacklimit 00465 *(ULONG *)StackLimit = PtrToUlong((PUCHAR)NextPage); 00466 } else { 00467 *StackLimit = (PVOID)( (PUCHAR)NextPage); 00468 } 00469 #else 00470 *StackLimit = (PVOID)( (PUCHAR)NextPage); 00471 #endif 00472 00473 } 00474 00475 return STATUS_STACK_OVERFLOW; 00476 } 00477 #if defined(_WIN64) 00478 if (Teb32) { 00479 // Update the 32-bit stacklimit 00480 *(ULONG *)StackLimit = PtrToUlong((PUCHAR)(NextPage + PAGE_SIZE)); 00481 } else { 00482 *StackLimit = (PVOID)((PUCHAR)(NextPage + PAGE_SIZE)); 00483 } 00484 #else 00485 *StackLimit = (PVOID)((PUCHAR)(NextPage + PAGE_SIZE)); 00486 #endif 00487 00488 #if defined(_IA64_) 00489 } 00490 #endif // _IA64_ 00491 00492 retry: 00493 status = ZwAllocateVirtualMemory (NtCurrentProcess(), 00494 (PVOID *)&NextPage, 00495 0, 00496 &RegionSize, 00497 MEM_COMMIT, 00498 PAGE_READWRITE | PAGE_GUARD); 00499 00500 00501 if (NT_SUCCESS(status) || (status == STATUS_ALREADY_COMMITTED)) { 00502 00503 // 00504 // The guard page is now committed or stack space is 00505 // already present, return success. 00506 // 00507 00508 return STATUS_PAGE_FAULT_GUARD_PAGE; 00509 } 00510 00511 if (PsGetCurrentProcess() == ExpDefaultErrorPortProcess) { 00512 00513 // 00514 // Don't let CSRSS process get any stack overflows due to 00515 // commitment. Increase the commitment by a page and 00516 // try again. 00517 // 00518 00519 ASSERT (status == STATUS_COMMITMENT_LIMIT); 00520 00521 ExAcquireSpinLock (&MmChargeCommitmentLock, &OldIrql); 00522 MmTotalCommitLimit += 1; 00523 MmExtendedCommit += 1; 00524 ExReleaseSpinLock (&MmChargeCommitmentLock, OldIrql); 00525 goto retry; 00526 } 00527 00528 return STATUS_STACK_OVERFLOW; 00529 00530 } except (EXCEPTION_EXECUTE_HANDLER) { 00531 00532 // 00533 // An exception has occurred during the referencing of the 00534 // TEB or TIB, just return a guard page violation and 00535 // don't deal with the stack overflow. 00536 // 00537 00538 return STATUS_GUARD_PAGE_VIOLATION; 00539 } 00540 }

Generated on Sat May 15 19:39:12 2004 for test by doxygen 1.3.7