752 lines
23 KiB
C
752 lines
23 KiB
C
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
critsect.c
|
|
|
|
Abstract:
|
|
|
|
This module implements verification functions for
|
|
critical section interfaces.
|
|
|
|
Author:
|
|
|
|
Daniel Mihai (DMihai) 27-Mar-2001
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
|
|
#include "verifier.h"
|
|
|
|
VOID
|
|
RtlpWaitForCriticalSection (
|
|
IN PRTL_CRITICAL_SECTION CriticalSection
|
|
);
|
|
|
|
//NTSYSAPI
|
|
BOOL
|
|
NTAPI
|
|
AVrfpRtlTryEnterCriticalSection(
|
|
PRTL_CRITICAL_SECTION CriticalSection
|
|
)
|
|
{
|
|
BOOL Result;
|
|
HANDLE CurrentThread;
|
|
LONG LockCount;
|
|
|
|
if ((AVrfpProvider.VerifierFlags & RTL_VRF_FLG_LOCK_CHECKS) != 0 &&
|
|
RtlDllShutdownInProgress() == FALSE ) {
|
|
|
|
//
|
|
// Sanity test for DebugInfo.
|
|
//
|
|
|
|
if (CriticalSection->DebugInfo == NULL) {
|
|
|
|
//
|
|
// This critical section is not initialized.
|
|
//
|
|
|
|
VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_NOT_INITIALIZED,
|
|
"critical section not initialized",
|
|
CriticalSection, "Critical section address",
|
|
CriticalSection->DebugInfo, "Critical section debug info address",
|
|
NULL, "",
|
|
NULL, "");
|
|
}
|
|
|
|
CurrentThread = NtCurrentTeb()->ClientId.UniqueThread;
|
|
|
|
LockCount = InterlockedCompareExchange( &CriticalSection->LockCount,
|
|
0,
|
|
-1 );
|
|
if (LockCount == -1) {
|
|
|
|
//
|
|
// The critical section was unowned and we just acquired it
|
|
//
|
|
|
|
//
|
|
// Sanity test for the OwningThread.
|
|
//
|
|
|
|
if (CriticalSection->OwningThread != 0) {
|
|
|
|
//
|
|
// The loader lock gets handled differently, so don't assert on it.
|
|
//
|
|
|
|
if (CriticalSection != NtCurrentPeb()->LoaderLock ||
|
|
CriticalSection->OwningThread != CurrentThread) {
|
|
|
|
//
|
|
// OwningThread should have been 0.
|
|
//
|
|
|
|
VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_INVALID_OWNER,
|
|
"invalid critical section owner thread",
|
|
CriticalSection, "Critical section address",
|
|
CriticalSection->OwningThread, "Owning thread",
|
|
0, "Expected owning thread",
|
|
CriticalSection->DebugInfo, "Critical section debug info address");
|
|
}
|
|
}
|
|
|
|
//
|
|
// Sanity test for the RecursionCount.
|
|
//
|
|
|
|
if (CriticalSection->RecursionCount != 0) {
|
|
|
|
//
|
|
// The loader lock gets handled differently, so don't assert on it.
|
|
//
|
|
|
|
if (CriticalSection != NtCurrentPeb()->LoaderLock) {
|
|
|
|
//
|
|
// RecursionCount should have been 0.
|
|
//
|
|
|
|
VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_INVALID_RECURSION_COUNT,
|
|
"invalid critical section recursion count",
|
|
CriticalSection, "Critical section address",
|
|
CriticalSection->RecursionCount, "Recursion count",
|
|
0, "Expected recursion count",
|
|
CriticalSection->DebugInfo, "Critical section debug info address");
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the critical section owner
|
|
//
|
|
|
|
CriticalSection->OwningThread = CurrentThread;
|
|
|
|
//
|
|
// Set the recursion count
|
|
//
|
|
// ntdll\ia64\critsect.s is using RecursionCount = 0 first time
|
|
// the current thread is acquiring the critical section.
|
|
//
|
|
// ntdll\ia64\critsect.asm is using RecursionCount = 1 first time
|
|
// the current thread is acquiring the critical section.
|
|
//
|
|
|
|
#if defined(_IA64_)
|
|
CriticalSection->RecursionCount = 0;
|
|
#else //#if defined(_IA64_)
|
|
CriticalSection->RecursionCount = 1;
|
|
#endif
|
|
|
|
#if DBG
|
|
//
|
|
// In a chk build we are updating this additional counter,
|
|
// just like the original function in ntdll does.
|
|
//
|
|
|
|
NtCurrentTeb()->CountOfOwnedCriticalSections += 1;
|
|
#endif
|
|
|
|
//
|
|
// All done, CriticalSection is owned by the current thread.
|
|
//
|
|
|
|
Result = TRUE;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// The critical section is currently owned by the current or another thread.
|
|
//
|
|
|
|
if (CriticalSection->OwningThread == CurrentThread) {
|
|
|
|
//
|
|
// The current thread is already the owner.
|
|
//
|
|
|
|
//
|
|
// Inrelock increment the LockCount, and increment the RecursionCount.
|
|
//
|
|
|
|
InterlockedIncrement (&CriticalSection->LockCount);
|
|
|
|
|
|
CriticalSection->RecursionCount += 1;
|
|
|
|
//
|
|
// All done, CriticalSection was already owned by
|
|
// the current thread and we have just incremented the RecursionCount.
|
|
//
|
|
|
|
Result = TRUE;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Another thread is the owner of this critical section.
|
|
//
|
|
|
|
Result = FALSE;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// The critical section verifier is not enabled
|
|
//
|
|
|
|
Result = RtlTryEnterCriticalSection (CriticalSection);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
//NTSYSAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
AVrfpRtlEnterCriticalSection(
|
|
volatile RTL_CRITICAL_SECTION *CriticalSection
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE CurrentThread;
|
|
LONG LockCount;
|
|
ULONG_PTR SpinCount;
|
|
|
|
if ((AVrfpProvider.VerifierFlags & RTL_VRF_FLG_LOCK_CHECKS) != 0 &&
|
|
RtlDllShutdownInProgress() == FALSE ) {
|
|
|
|
//
|
|
// Sanity test for DebugInfo.
|
|
//
|
|
|
|
if (CriticalSection->DebugInfo == NULL) {
|
|
|
|
//
|
|
// This critical section is not initialized.
|
|
//
|
|
|
|
VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_NOT_INITIALIZED,
|
|
"critical section not initialized",
|
|
CriticalSection, "Critical section address",
|
|
CriticalSection->DebugInfo, "Critical section debug info address",
|
|
NULL, "",
|
|
NULL, "");
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
CurrentThread = NtCurrentTeb()->ClientId.UniqueThread;
|
|
|
|
SpinCount = CriticalSection->SpinCount;
|
|
|
|
if (SpinCount == 0) {
|
|
|
|
//
|
|
// Zero spincount for this critical section.
|
|
//
|
|
|
|
EnterZeroSpinCount:
|
|
|
|
LockCount = InterlockedIncrement (&CriticalSection->LockCount);
|
|
|
|
if (LockCount == 0) {
|
|
|
|
EnterSetOwnerAndRecursion:
|
|
|
|
//
|
|
// The current thread is the new owner of the critical section.
|
|
//
|
|
|
|
//
|
|
// Sanity test for the OwningThread.
|
|
//
|
|
|
|
if (CriticalSection->OwningThread != 0) {
|
|
|
|
//
|
|
// The loader lock gets handled differently, so don't assert on it.
|
|
//
|
|
|
|
if (CriticalSection != NtCurrentPeb()->LoaderLock ||
|
|
CriticalSection->OwningThread != CurrentThread) {
|
|
|
|
//
|
|
// OwningThread should have been 0.
|
|
//
|
|
|
|
VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_INVALID_OWNER,
|
|
"invalid critical section owner thread",
|
|
CriticalSection, "Critical section address",
|
|
CriticalSection->OwningThread, "Owning thread",
|
|
0, "Expected owning thread",
|
|
CriticalSection->DebugInfo, "Critical section debug info address");
|
|
}
|
|
}
|
|
|
|
//
|
|
// Sanity test for the RecursionCount.
|
|
//
|
|
|
|
if (CriticalSection->RecursionCount != 0) {
|
|
|
|
//
|
|
// The loader lock gets handled differently, so don't assert on it.
|
|
//
|
|
|
|
if (CriticalSection != NtCurrentPeb()->LoaderLock) {
|
|
|
|
//
|
|
// RecursionCount should have been 0.
|
|
//
|
|
|
|
VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_INVALID_RECURSION_COUNT,
|
|
"invalid critical section recursion count",
|
|
CriticalSection, "Critical section address",
|
|
CriticalSection->RecursionCount, "Recursion count",
|
|
0, "Expected recursion count",
|
|
CriticalSection->DebugInfo, "Critical section debug info address");
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the critical section owner
|
|
//
|
|
|
|
CriticalSection->OwningThread = CurrentThread;
|
|
|
|
//
|
|
// Set the recursion count
|
|
//
|
|
// ntdll\ia64\critsect.s is using RecursionCount = 0 first time
|
|
// the current thread is acquiring the critical section.
|
|
//
|
|
// ntdll\ia64\critsect.asm is using RecursionCount = 1 first time
|
|
// the current thread is acquiring the critical section.
|
|
//
|
|
|
|
#if defined(_IA64_)
|
|
CriticalSection->RecursionCount = 0;
|
|
#else //#if defined(_IA64_)
|
|
CriticalSection->RecursionCount = 1;
|
|
#endif
|
|
|
|
#if DBG
|
|
//
|
|
// In a chk build we are updating these additional counters,
|
|
// just like the original function in ntdll does.
|
|
//
|
|
|
|
NtCurrentTeb()->CountOfOwnedCriticalSections += 1;
|
|
CriticalSection->DebugInfo->EntryCount += 1;
|
|
#endif
|
|
|
|
//
|
|
// All done, CriticalSection is owned by the current thread.
|
|
//
|
|
}
|
|
else if (LockCount >= 0) {
|
|
|
|
//
|
|
// The critical section is currently owned by the current or another thread.
|
|
//
|
|
|
|
if (CriticalSection->OwningThread == CurrentThread) {
|
|
|
|
//
|
|
// The current thread is already the owner.
|
|
//
|
|
|
|
CriticalSection->RecursionCount += 1;
|
|
|
|
#if DBG
|
|
//
|
|
// In a chk build we are updating this additional counter,
|
|
// just like the original function in ntdll does.
|
|
//
|
|
|
|
CriticalSection->DebugInfo->EntryCount += 1;
|
|
#endif
|
|
|
|
//
|
|
// All done, CriticalSection was already owned by
|
|
// the current thread and we have just incremented the RecursionCount.
|
|
//
|
|
}
|
|
else {
|
|
|
|
//
|
|
// The current thread is not the owner. Wait for ownership
|
|
//
|
|
|
|
RtlpWaitForCriticalSection ((PRTL_CRITICAL_SECTION)CriticalSection);
|
|
|
|
//
|
|
// We have just aquired the critical section.
|
|
//
|
|
|
|
goto EnterSetOwnerAndRecursion;
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// The original LockCount was < -1 so the critical section was
|
|
// over-released or corrupted.
|
|
//
|
|
|
|
VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_OVER_RELEASED,
|
|
"critical section over-released or corrupted",
|
|
CriticalSection, "Critical section address",
|
|
CriticalSection->LockCount, "Lock count",
|
|
0, "Expected minimum lock count",
|
|
CriticalSection->DebugInfo, "Critical section debug info address");
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// SpinCount > 0 for this critical section
|
|
//
|
|
|
|
if( CriticalSection->OwningThread == CurrentThread ) {
|
|
|
|
//
|
|
// The current thread is already the owner.
|
|
//
|
|
|
|
InterlockedIncrement( &CriticalSection->LockCount );
|
|
|
|
CriticalSection->RecursionCount += 1;
|
|
|
|
#if DBG
|
|
//
|
|
// In a chk build we are updating this additional counter,
|
|
// just like the original function in ntdll does.
|
|
//
|
|
|
|
CriticalSection->DebugInfo->EntryCount += 1;
|
|
#endif
|
|
|
|
//
|
|
// All done, CriticalSection was already owned by the current thread
|
|
// and we have just incremented the LockCount and RecursionCount.
|
|
//
|
|
}
|
|
else {
|
|
|
|
//
|
|
// The current thread is not the owner. Attempt to acquire.
|
|
//
|
|
|
|
EnterTryAcquire:
|
|
|
|
LockCount = InterlockedCompareExchange( &CriticalSection->LockCount,
|
|
0,
|
|
-1 );
|
|
|
|
if (LockCount == -1) {
|
|
|
|
//
|
|
// We have just aquired the critical section.
|
|
//
|
|
|
|
goto EnterSetOwnerAndRecursion;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Look if there are already other threads spinning while
|
|
// waiting for this critical section.
|
|
//
|
|
|
|
if (CriticalSection->LockCount >= 1) {
|
|
|
|
//
|
|
// There are other waiters for this critical section.
|
|
// Do not spin, just wait for the critical section to be
|
|
// released as if we had 0 spin count from the beginning.
|
|
//
|
|
|
|
goto EnterZeroSpinCount;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// No other threads are waiting for this critical section.
|
|
//
|
|
|
|
EnterSpinOnLockCount:
|
|
|
|
if (CriticalSection->LockCount == -1) {
|
|
|
|
//
|
|
// We have a chance for aquiring it now
|
|
//
|
|
|
|
goto EnterTryAcquire;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// The critical section is still owned.
|
|
// Decrement the spin count and decide if we should continue
|
|
// to spin or simply wait for the critical section's event.
|
|
//
|
|
|
|
SpinCount -= 0;
|
|
|
|
if (SpinCount > 0) {
|
|
|
|
//
|
|
// Spin
|
|
//
|
|
|
|
goto EnterSpinOnLockCount;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Spun enough, just wait for the critical section to be
|
|
// released as if we had 0 spin count from the beginning.
|
|
//
|
|
|
|
goto EnterZeroSpinCount;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// The critical section verifier is not enabled
|
|
//
|
|
|
|
Status = RtlEnterCriticalSection ((PRTL_CRITICAL_SECTION)CriticalSection);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
//NTSYSAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
AVrfpRtlLeaveCriticalSection(
|
|
volatile RTL_CRITICAL_SECTION *CriticalSection
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE CurrentThread;
|
|
|
|
if ((AVrfpProvider.VerifierFlags & RTL_VRF_FLG_LOCK_CHECKS) != 0 &&
|
|
RtlDllShutdownInProgress() == FALSE) {
|
|
|
|
//
|
|
// Sanity test for DebugInfo.
|
|
//
|
|
|
|
if (CriticalSection->DebugInfo == NULL) {
|
|
|
|
//
|
|
// This critical section is not initialized.
|
|
//
|
|
|
|
VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_NOT_INITIALIZED,
|
|
"critical section not initialized",
|
|
CriticalSection, "Critical section address",
|
|
CriticalSection->DebugInfo, "Critical section debug info address",
|
|
NULL, "",
|
|
NULL, "");
|
|
}
|
|
|
|
//
|
|
// Verify that the critical section is locked before releasing.
|
|
//
|
|
|
|
if (CriticalSection->LockCount < 0) {
|
|
|
|
//
|
|
// The critical section is not locked
|
|
//
|
|
|
|
VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_OVER_RELEASED,
|
|
"critical section over-released or corrupted",
|
|
CriticalSection, "Critical section address",
|
|
CriticalSection->LockCount, "Lock count",
|
|
0, "Expected minimum lock count",
|
|
CriticalSection->DebugInfo, "Critical section debug info address");
|
|
}
|
|
|
|
//
|
|
// Verify that the current thread owns the critical section.
|
|
//
|
|
|
|
CurrentThread = NtCurrentTeb()->ClientId.UniqueThread;
|
|
|
|
if (CriticalSection->OwningThread != CurrentThread) {
|
|
|
|
VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_INVALID_OWNER,
|
|
"invalid critical section owner thread",
|
|
CriticalSection, "Critical section address",
|
|
CriticalSection->OwningThread, "Owning thread",
|
|
CurrentThread, "Expected owning thread",
|
|
CriticalSection->DebugInfo, "Critical section debug info address");
|
|
}
|
|
|
|
//
|
|
// Verify the recursion count.
|
|
//
|
|
// ntdll\ia64\critsect.s is using RecursionCount = 0 first time
|
|
// the current thread is acquiring the critical section.
|
|
//
|
|
// ntdll\ia64\critsect.asm is using RecursionCount = 1 first time
|
|
// the current thread is acquiring the critical section.
|
|
//
|
|
|
|
|
|
#if defined(_IA64_)
|
|
|
|
if (CriticalSection->RecursionCount < 0) {
|
|
VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_INVALID_RECURSION_COUNT,
|
|
"invalid critical section recursion count",
|
|
CriticalSection, "Critical section address",
|
|
CriticalSection->RecursionCount, "Recursion count",
|
|
0, "Expected minimum recursion count",
|
|
CriticalSection->DebugInfo, "Critical section debug info address");
|
|
}
|
|
|
|
#else //#if defined(_IA64_)
|
|
|
|
if (CriticalSection->RecursionCount < 1) {
|
|
VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_INVALID_RECURSION_COUNT,
|
|
"invalid critical section recursion count",
|
|
CriticalSection, "Critical section address",
|
|
CriticalSection->RecursionCount, "Recursion count",
|
|
1, "Expected minimum recursion count",
|
|
CriticalSection->DebugInfo, "Critical section debug info address");
|
|
}
|
|
#endif //#if defined(_IA64_)
|
|
}
|
|
|
|
Status = RtlLeaveCriticalSection ((PRTL_CRITICAL_SECTION)CriticalSection);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//NTSYSAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
AVrfpRtlInitializeCriticalSection(
|
|
PRTL_CRITICAL_SECTION CriticalSection
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// If we could have pointers to ntdll!RtlCriticalSectionLock and
|
|
// RtlCriticalSectionList we could check for double-initialized
|
|
// critical sections here.
|
|
//
|
|
|
|
Status = RtlInitializeCriticalSection (CriticalSection);
|
|
|
|
return Status;
|
|
}
|
|
|
|
//NTSYSAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
AVrfpRtlInitializeCriticalSectionAndSpinCount(
|
|
PRTL_CRITICAL_SECTION CriticalSection,
|
|
ULONG SpinCount
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// If we could have pointers to ntdll!RtlCriticalSectionLock and
|
|
// RtlCriticalSectionList we could check for double-initialized
|
|
// critical sections here.
|
|
//
|
|
|
|
Status = RtlInitializeCriticalSectionAndSpinCount (CriticalSection,
|
|
SpinCount);
|
|
return Status;
|
|
}
|
|
|
|
//NTSYSAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
AVrfpRtlDeleteCriticalSection(
|
|
PRTL_CRITICAL_SECTION CriticalSection
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
if ((AVrfpProvider.VerifierFlags & RTL_VRF_FLG_LOCK_CHECKS) != 0 &&
|
|
RtlDllShutdownInProgress() == FALSE ) {
|
|
|
|
//
|
|
// Sanity test for DebugInfo.
|
|
//
|
|
|
|
if (CriticalSection->DebugInfo == NULL) {
|
|
|
|
//
|
|
// This critical section is not initialized.
|
|
//
|
|
|
|
VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_NOT_INITIALIZED,
|
|
"critical section not initialized",
|
|
CriticalSection, "Critical section address",
|
|
CriticalSection->DebugInfo, "Critical section debug info address",
|
|
NULL, "",
|
|
NULL, "");
|
|
}
|
|
|
|
//
|
|
// Verify that no thread owns or waits for this critical section or
|
|
// the owner is the current thread.
|
|
//
|
|
// ntdll\ia64\critsect.s is using RecursionCount = 0 first time
|
|
// the current thread is acquiring the critical section.
|
|
//
|
|
// ntdll\ia64\critsect.asm is using RecursionCount = 1 first time
|
|
// the current thread is acquiring the critical section.
|
|
//
|
|
|
|
if (CriticalSection->LockCount != -1 &&
|
|
(CriticalSection->OwningThread != NtCurrentTeb()->ClientId.UniqueThread ||
|
|
#if defined(_IA64_)
|
|
CriticalSection->RecursionCount < 0) ) {
|
|
#else
|
|
CriticalSection->RecursionCount < 1) ) {
|
|
#endif //#if defined(_IA64_)
|
|
|
|
VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_INVALID_LOCK_COUNT,
|
|
"deleting critical section with invalid lock count",
|
|
CriticalSection, "Critical section address",
|
|
CriticalSection->LockCount, "Lock count",
|
|
-1, "Expected lock count",
|
|
CriticalSection->OwningThread, "Owning thread");
|
|
}
|
|
}
|
|
|
|
Status = RtlDeleteCriticalSection (CriticalSection);
|
|
|
|
return Status;
|
|
}
|
|
|