windows-nt/Source/XPSP1/NT/drivers/wdm/audio/legacy/wdmaud.sys/robust.c

422 lines
11 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
#include "wdmsys.h"
#pragma LOCKED_CODE
#pragma LOCKED_DATA
//
// Robust checking enables bugchecks in the retail version of this component.
//
BOOL gbRobustChecking = FALSE;
#if 0
//
// PagedCode: This routine should be used in routines that touch pageable
// code and data. Notice that this is not the PAGED_CODE() macro so that it
// will be in the retail code.
//
void
PagedCode(
void
)
{
if( KeGetCurrentIrql() > APC_LEVEL )
{
KeBugCheckEx(AUDIO_BUGCHECK_CODE,AUDIO_NOT_BELOW_DISPATCH_LEVEL,0,0,0);
}
}
void
ValidatePassiveLevel(
void
)
{
if( KeGetCurrentIrql() != PASSIVE_LEVEL )
{
KeBugCheckEx(AUDIO_BUGCHECK_CODE,AUDIO_NOT_AT_PASSIVE_LEVEL,0,0,0);
}
}
//
// If bExclusive is TRUE, the caller wants to know if the mutex is acquired multiple
// times on the same thread. If they don't want this checking done, they should
// set bExclusive to FALSE.
//
NTSTATUS
AudioEnterMutex(
IN PKMUTEX pmutex,
IN BOOL bExclusive
)
{
PRKTHREAD pkt;
NTSTATUS Status;
LONG lMutexState;
Status = KeWaitForSingleObject ( pmutex,Executive,KernelMode,FALSE,NULL ) ;
if( gbRobustChecking && bExclusive )
{
//
// After entering the mutex, we sanity check our nested count, but we
// only do this if we need to. If the caller said that they designed the
// code only for exclusive access, we'll bugcheck if we get nested.
//
if( (lMutexState = KeReadStateMutex(pmutex)) < 0 )
{
//
// Every time you acquire a mutex the state will be decremented. 1 means
// that it's available, 0 means it's held, -1 means this is the second time
// it's acquired, -2 means it's the third and so on. Thus, if we're nested
// on this thread, we'll end up here.
//
KeBugCheckEx(AUDIO_BUGCHECK_CODE,
AUDIO_NESTED_MUTEX_SITUATION,
(ULONG_PTR)pmutex,
lMutexState,
0);
}
}
return Status;
}
//
// This routine yields if we're at PASSIVE_LEVEL
//
void
AudioPerformYield(
void
)
{
PRKTHREAD pthrd;
KPRIORITY kpriority;
//
// After releasing the mutex we want to yield execution to all other threads
// on the machine to try to expose preemption windows.
//
if( ( gbRobustChecking ) &&
( KeGetCurrentIrql() == PASSIVE_LEVEL ) )
{
if( (pthrd = KeGetCurrentThread()) )
{
kpriority = KeQueryPriorityThread(KeGetCurrentThread());
//
// Lower this thrread's priority so another thread will
// get scheduled.
//
KeSetPriorityThread(pthrd,1); // Is that low enough?
//
// This might be overkill, but yield.
//
ZwYieldExecution();
//
// Now restore the priority for this thread and party on.
//
KeSetPriorityThread(pthrd,kpriority);
}
}
}
void
AudioLeaveMutex(
IN PKMUTEX pmutex
)
{
PRKTHREAD pthrd;
KPRIORITY kpriority;
KeReleaseMutex ( pmutex, FALSE ) ;
AudioPerformYield();
}
NTSTATUS
AudioIoCallDriver (
IN PDEVICE_OBJECT pDevice,
IN PIRP pIrp
)
{
NTSTATUS Status;
Status = IoCallDriver(pDevice,pIrp);
AudioPerformYield();
return Status;
}
void
AudioEnterSpinLock(
IN PKSPIN_LOCK pSpinLock,
OUT PKIRQL pOldIrql
)
{
//
// KeAcquireSpinLock can only be called less then or equal to dispatch level.
// let's verify that here.
//
if( ( gbRobustChecking ) &&
( KeGetCurrentIrql() > DISPATCH_LEVEL ) )
{
KeBugCheckEx(AUDIO_BUGCHECK_CODE,
AUDIO_INVALID_IRQL_LEVEL,
0,
0,
0);
}
KeAcquireSpinLock ( pSpinLock, pOldIrql ) ;
}
void
AudioLeaveSpinLock(
IN PKSPIN_LOCK pSpinLock,
IN KIRQL OldIrql
)
{
KeReleaseSpinLock ( pSpinLock, OldIrql ) ;
AudioPerformYield();
}
void
AudioObDereferenceObject(
IN PVOID pvObject
)
{
ObDereferenceObject(pvObject);
AudioPerformYield();
}
void
AudioIoCompleteRequest(
IN PIRP pIrp,
IN CCHAR PriorityBoost
)
{
IoCompleteRequest(pIrp,PriorityBoost);
AudioPerformYield();
}
#endif
#define MEMORY_LIMIT_CHECK 262144
//
// This routine assumes that pptr points to a memory location that currently
// contains a NULL value. Thus, on failure, this routine does not have to
// write back a NULL value.
//
// Entry:
// if dwFlags contains:
//#define DEFAULT_MEMORY 0x00 // Standard ExAllocatePool call
//#define ZERO_FILL_MEMORY 0x01 // Zero the memory
//#define QUOTA_MEMORY 0x02 // ExAllocatePoolWithQuota call
//#define LIMIT_MEMORY 0x04 // Never allocation more then 1/4 Meg
//#define FIXED_MEMORY 0x08 // Use locked memory
//#define PAGED_MEMORY 0x10 // use pageable memory
//
NTSTATUS
AudioAllocateMemory(
IN SIZE_T bytes,
IN ULONG tag,
IN AAMFLAGS dwFlags,
OUT PVOID *pptr
)
{
NTSTATUS Status;
POOL_TYPE pooltype;
PVOID pInit;
KIRQL irql;
PVOID ptr = NULL;
ASSERT(*pptr == NULL);
if( 0 == bytes )
{
//
// The code should never be asking for zero bytes. The core will bugcheck
// on a call like this. In debug mode we'll assert. In retail, we'll
// return an error.
//
ASSERT(0);
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
if( dwFlags & FIXED_MEMORY )
pooltype = NonPagedPool;
else
pooltype = PagedPool;
//
// Allocating pageable memory at DISPATCH_LEVEL is a bad thing to do.
// here we make sure that is not the case.
//
if( ( (irql = KeGetCurrentIrql()) > DISPATCH_LEVEL ) ||
((DISPATCH_LEVEL == irql ) && (NonPagedPool != pooltype)) )
{
//
// Either way, we've got an error, bugcheck or exit.
//
if( gbRobustChecking )
{
KeBugCheckEx(AUDIO_BUGCHECK_CODE,AUDIO_INVALID_IRQL_LEVEL,0,0,0);
} else {
//
// If we can't bugcheck, then we're going to return an error
// in this case.
//
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
}
//
// Let's see if the caller has asked to limit the allocation to a "reasonable"
// number of bytes. If so, "reasonable" is 1/4 meg.
//
if( ( dwFlags & LIMIT_MEMORY ) && ( bytes > MEMORY_LIMIT_CHECK ) )
{
//
// For some reason, this allocation is trying to allocate more then was designed.
// Why is the caller allocating so much?
//
if( gbRobustChecking )
KeBugCheckEx(AUDIO_BUGCHECK_CODE,AUDIO_ABSURD_ALLOCATION_ATTEMPTED,0,0,0);
//
// Checked build will assert if robust checking is not enabled.
//
ASSERT("Memory Allocation Unreasonable!");
}
//
// Allocate the memory, NULL is returned on failure in the normal case, an exception
// is raised with quota. ptr is NULLed out above for the Quota routine.
//
if( dwFlags & QUOTA_MEMORY )
{
//
// Caller wants to charge the allocation to the current user context.
//
try
{
ptr = ExAllocatePoolWithQuotaTag(pooltype,bytes,tag);
}except (EXCEPTION_EXECUTE_HANDLER) {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
} else {
//
// Don't want to assign this memory to the quota.
//
ptr = ExAllocatePoolWithTag(pooltype, bytes, tag);
}
if( ptr )
{
if( dwFlags & ZERO_FILL_MEMORY )
{
RtlFillMemory( ptr,bytes,0 );
}
//
// Never allocate over the top of an existing pointer. Interlock exchange
// with the location. This interlock updates the location in the caller.
//
if( pInit = InterlockedExchangePointer(pptr,ptr) )
{
//
// If we get a return value from this exchange it means that there
// was already a pointer in the location that we added a pointer too.
// If this is the case, most likely we overwrote a valid memory
// pointer. With robust checking we don't want this to happen.
//
if( gbRobustChecking )
{
KeBugCheckEx(AUDIO_BUGCHECK_CODE,
AUDIO_MEMORY_ALLOCATION_OVERWRITE,
(ULONG_PTR)pInit,
0,0);
}
//
// If we end up here, we've overwritten a memory pointer with a
// successful memory allocation. Or ASSERT at the top of this
// function should have fired. In any case, we can return success
// and how that nothing bad happens.
//
}
Status = STATUS_SUCCESS;
} else {
//
// Our memory allocation failed. *pptr should still be NULL
// return error.
//
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
exit:
return Status;
}
//
// This routine is safe to call with a NULL memory pointer. It will return
// STATUS_UNSUCCESSFUL and do nothing if passed a NULL pointer. Also, this routine
// reads the location atomically so it should be safe in a multiproc environment.
//
void
AudioFreeMemory(
IN SIZE_T bytes,
IN OUT PVOID *pptr
)
{
PVOID pFree;
KIRQL irql;
//
// Interlockedexhchange the pointer will NULL, validate that it's non-NULL
// trash the memory if needed and then free the pointer.
//
pFree = InterlockedExchangePointer(pptr,NULL);
if( pFree )
{
//
// We have a pointer to free.
//
if( gbRobustChecking )
{
//
// The docs say that we need to be careful how we call the free routine
// with regard to IRQlevel. Thus, if we're being robust we'll do this
// extra work.
//
if( (irql = KeGetCurrentIrql()) > DISPATCH_LEVEL )
{
KeBugCheckEx(AUDIO_BUGCHECK_CODE,AUDIO_INVALID_IRQL_LEVEL,0,0,0);
}
//
// Under debug we're going to put 'k's in the memory location in order
// to make extra sure that no one is using this memory.
//
if( UNKNOWN_SIZE != bytes )
{
RtlFillMemory( pFree,bytes,'k' );
}
}
//
// Now we actually free the memory.
//
ExFreePool(pFree);
}
return;
}