Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prefast annotations to fix IRQL level warnings

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.

like image 239
Daniel Knueven Avatar asked May 17 '16 02:05

Daniel Knueven


1 Answers

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);
}
like image 189
M'hand BOUGHIAS Avatar answered Oct 14 '22 15:10

M'hand BOUGHIAS