I am writing a device driver for windows 7 32 bit. I'm using the WDK version 7600.16385.1. So far things are going well, but prefast keeps telling me I'm messing up the IRQL level. In-particular when I try to lock/unlock a shared buffer.
I have a structure that represents a buffer like so:
typedef struct _PORT_BUFFER {
WDFMEMORY mMemory;
PUCHAR pucBuff;
ULONG ulSizeMax;
ULONG ulSizeCurr;
ULONG ulAdd;
ULONG ulRemove;
ULONG ulLost;
WDFREQUEST rPending;
BOOLEAN bDMAing;
WDFSPINLOCK slLock;
} PORT_BUFFER, *PPORT_BUFFER;
I have two functions that can lock and unlock said buffers:
VOID PLxBufferLock(PPORT_BUFFER ppbBuff){
WdfSpinLockAcquire(ppbBuff->slLock);
}
VOID PLxBufferUnlock(PPORT_BUFFER ppbBuff){
WdfSpinLockRelease(ppbBuff->slLock);
}
When I compile my driver, prefast tells me:
warning 28167 : The function 'PLxBufferLock' changes the IRQL and does not restore the IRQL before it exits. It should be annotated to reflect the change or the IRQL should be restored. IRQL was last set to 2 at line 57.
warning 28167 : The function 'PLxBufferUnlock' changes the IRQL and does not restore the IRQL before it exits. It should be annotated to reflect the change or the IRQL should be restored. IRQL was last set at line 63.
So, I looked at how WdfSpinLockAcquire and WdfSpinLockRelease are defined:
__drv_raisesIRQL(DISPATCH_LEVEL)
__drv_maxIRQL(DISPATCH_LEVEL)
VOID
FORCEINLINE
WdfSpinLockAcquire(
__in
__drv_savesIRQL
__drv_neverHold(SpinLockObj)
__drv_acquiresResource(SpinLockObj)
WDFSPINLOCK SpinLock
)
{
((PFN_WDFSPINLOCKACQUIRE) WdfFunctions[WdfSpinLockAcquireTableIndex])(WdfDriverGlobals, SpinLock);
}
__drv_maxIRQL(DISPATCH_LEVEL)
__drv_minIRQL(DISPATCH_LEVEL)
VOID
FORCEINLINE
WdfSpinLockRelease(
__in
__drv_restoresIRQL
__drv_mustHold(SpinLockObj)
__drv_releasesResource(SpinLockObj)
WDFSPINLOCK SpinLock
)
{
((PFN_WDFSPINLOCKRELEASE) WdfFunctions[WdfSpinLockReleaseTableIndex])(WdfDriverGlobals, SpinLock);
}
Seems pretty straight forward. So I changed my functions to look the same:
__drv_raisesIRQL(DISPATCH_LEVEL)
__drv_maxIRQL(DISPATCH_LEVEL)
VOID PLxBufferLock(
__in
__drv_savesIRQL
__drv_neverHold(ppbBuff)
__drv_acquiresResource(ppbBuff)
PPORT_BUFFER ppbBuff);
__drv_maxIRQL(DISPATCH_LEVEL)
__drv_minIRQL(DISPATCH_LEVEL)
VOID PLxBufferUnlock(
__in
__drv_restoresIRQL
__drv_mustHold(ppbBuff)
__drv_releasesResource(ppbBuff)
PPORT_BUFFER ppbBuff);
Then I go from two warnings to many many warnings about leaking ppbBuff and still not restoring the IRQL level correctly:
warning 28103 : Leaking the ppbBuff stored in 'ppbBuff'.
warning 28104 : The ppbBuff that should have been acquired before function exit was not acquired.
warning 28107 : The ppbBuff '&pdepPort->pbRead' must be held when calling 'PLxBufferUnlock'.
warning 28107 : The ppbBuff '&pdepPort->pbWrite' must be held when calling 'PLxBufferUnlock'.
warning 28107 : The ppbBuff '&pdepExtPort->pbRead' must be held when calling 'PLxBufferUnlock'.
warning 28107 : The ppbBuff '&pdepExtPort->pbRead' must be held when calling 'PLxBufferUnlock'.
warning 28107 : The ppbBuff '&pdepExtPort->pbWrite' must be held when calling 'PLxBufferUnlock'.
warning 28157 : The IRQL in 'ppbBuff' was never restored.
warning 28158 : No IRQL was saved into 'ppbBuff'.
warning 28166 : The function 'PLxInitEvtPortCleanup' does not restore the IRQL to the value that was current at function entry and is required to do so. IRQL was last set at line 320.
warning 28166 : The function 'PLxReadEvt' does not restore the IRQL to the value that was current at function entry and is required to do so. IRQL was last set at line 51.
warning 28166 : The function 'PLxReadEvtTimer' does not restore the IRQL to the value that was current at function entry and is required to do so. IRQL was last set at line 104.
warning 28166 : The function 'PLxWriteEvt' does not restore the IRQL to the value that was current at function entry and is required to do so. IRQL was last set at line 60.
I use the buffers and I'm not bug checking anywhere so I think I'm locking correctly. Anyone have any idea how to appease prefast in this case? Thanks!
EDIT:
Here's an example where I use the locking functions:
VOID PLxInitEvtPortCleanup(WDFFILEOBJECT foFileObject){
PDEVICE_EXTENSION_PORT pdepPort = NULL;
PDEVICE_EXTENSION_CARD pdecCard = NULL;
GSCSIO4BXSYNC_PORT_CONFIGURATION pcPortConfig = { 0 };
WDFREQUEST rRequest = NULL;
pdepPort = PLxGetDeviceContextPort(WdfFileObjectGetDevice(foFileObject));
pdecCard = PLxGetDeviceContextCard(pdepPort->dDeviceCard);
pcPortConfig.bEnable = FALSE;
pcPortConfig.bRxEnable = FALSE;
pcPortConfig.bTxEnable = FALSE;
pcPortConfig.ulClockFrequency = 0;
pcPortConfig.eptcdTxClockDirection = GSCSIO4BXSYNC_ESTCD_INPUT;
PLxSioConfigPortSet(pdecCard,pdepPort->ulPortNumber,&pcPortConfig);
PLxBufferLock(&pdepPort->pbRead);
rRequest = PLxBufferClearPendingRequest(&pdepPort->pbRead);
PLxBufferUnlock(&pdepPort->pbRead);
if (rRequest) WdfRequestComplete(rRequest,STATUS_CANCELLED);
PLxBufferLock(&pdepPort->pbWrite);
rRequest = PLxBufferClearPendingRequest(&pdepPort->pbWrite);
PLxBufferUnlock(&pdepPort->pbWrite);
if (rRequest) WdfRequestComplete(rRequest,STATUS_CANCELLED);
}
I lock the buffer, remove any pending request, unlock the buffer and complete the request. Prefast tells me I'm not restoring the IRQL level correctly. When I comment out the lock/clear/unlock/complete code, prefast is happy again.
EDIT:
I've upgraded to VS2015 and WDK10+SDK10. I've added the annotations suggested by M'hand BOUGHIAS:
_Acquires_lock_(ppbBuff->slLock)
_Requires_lock_not_held_(ppbBuff->slLock)
_IRQL_saves_
VOID PLxBufferLock(PPORT_BUFFER ppbBuff){
WdfSpinLockAcquire(ppbBuff->slLock);
}
_Releases_lock_(ppbBuff->slLock)
_Requires_lock_held_(ppbBuff->slLock)
_IRQL_restores_
VOID PLxBufferUnlock(PPORT_BUFFER ppbBuff){
WdfSpinLockRelease(ppbBuff->slLock);
}
Now I'm getting the following:
warning C28158: No IRQL was saved into 'return'.
warning C28167: The function 'PLxBufferLock' changes the IRQL and does not restore the IRQL before it exits. It should be annotated to reflect the change or the IRQL should be restored.
warning C28157: The IRQL in 'return' was never restored.
I noticed the _Acquires_lock_ and _Requires_lock_not_held_ where defined as nothing so I looked at them and noticed they require _PREFAST_ to be defined to work. So I added _PREFAST_ to my preprocessor defines. Now I get a bunch of linking errors but no more prefast errors!
error LNK2005: __Lock_kind_mutex_ already defined in Buffer.obj
error LNK2005: __Lock_kind_event_ already defined in Buffer.obj
error LNK2005: __Lock_kind_semaphore_ already defined in Buffer.obj
error LNK2005: __Lock_kind_spin_lock_ already defined in Buffer.obj
error LNK2005: __Lock_kind_critical_section_ already defined in Buffer.obj
error LNK2001: unresolved external symbol __Prefast_unreferenced_parameter_impl_
I figured I messed something up while converting my project to VS2015. So I created the standard KMDF driver project with gives you a nice framework to build on. I turned on static analysis in the new project and defined _PREFAST_ as before and it also gives me the same linking errors.
You are on the good path. Indeed, you can tell prefast that your functions lock or unlock objects thanks to the SAL annotations : Annotating Locking Behavior
You also need to tell prefast about the IRQL change : IRQL annotations for drivers
Here is a sample code that uses the SAL annotations based on your code, they may be different from the old WDK7 :
_IRQL_raises_(DISPATCH_LEVEL)
_Acquires_lock_(ppbBuff->slLock)
_Requires_lock_not_held_(ppbBuff->slLock)
VOID PLxBufferLock(_In_ _IRQL_saves_ PPORT_BUFFER ppbBuff){
WdfSpinLockAcquire(ppbBuff->slLock);
}
_Releases_lock_(ppbBuff->slLock)
_Requires_lock_held_(ppbBuff->slLock)
VOID PLxBufferUnlock(_In_ _IRQL_restores_ PPORT_BUFFER ppbBuff){
WdfSpinLockRelease(ppbBuff->slLock);
}
Remark : You should use the annotation on the ppbBuff->slLock
variable, not on its owner.
UPDATE : After reading the above documentation I have update the use of the annotation, _IRQL_saves_/_IRQL_restores_
must be used on a function parameter not on the function. You may also need to use _IRQL_raises_
. I have edited the above code sample. You can also remove your _PREFAST_
definition to avoid the link issue. _PREFAST_
is defined by the compiler when it start the analysis.
UPDATE 2: Instead of using the _IRQL_saves_/_IRQL_restores
annotation on the _In_
parameter you can also use _IRQL_saves_global_(kind, param) / _IRQL_restores_global_(kind, param)
annotation on the function. Here is an updated code sample:
_IRQL_raises_(DISPATCH_LEVEL)
_Acquires_lock_(ppbBuff->slLock)
_Requires_lock_not_held_(ppbBuff->slLock)
_IRQL_saves_global_(SpinLock, ppbBuff)
VOID PLxBufferLock(_In_ PPORT_BUFFER ppbBuff){
WdfSpinLockAcquire(ppbBuff->slLock);
}
_Releases_lock_(ppbBuff->slLock)
_Requires_lock_held_(ppbBuff->slLock)
_IRQL_restores_global_(SpinLock, ppbBuff)
VOID PLxBufferUnlock(_In_ PPORT_BUFFER ppbBuff){
WdfSpinLockRelease(ppbBuff->slLock);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With