用于修复 IRQL 级别警告的 Prefast 注释

Prefast annotations to fix IRQL level warnings

我正在为 windows 7 32 位编写设备驱动程序。我使用的是 WDK 版本 7600.16385.1。到目前为止一切进展顺利,但 prefast 一直告诉我我弄乱了 IRQL 级别。特别是当我尝试 lock/unlock 共享缓冲区时。

我有一个表示缓冲区的结构,如下所示:

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;

我有两个函数可以锁定和解锁所述缓冲区:

VOID PLxBufferLock(PPORT_BUFFER ppbBuff){

    WdfSpinLockAcquire(ppbBuff->slLock);

}

VOID PLxBufferUnlock(PPORT_BUFFER ppbBuff){

    WdfSpinLockRelease(ppbBuff->slLock);

}

当我编译我的驱动程序时,prefast 告诉我:

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.

所以,我查看了 WdfSpinLockAcquire 和 WdfSpinLockRelease 是如何定义的:

__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);
}

看起来很简单。所以我改变了我的功能看起来一样:

__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);

然后我从两个警告变成许多关于泄漏 ppbBuff 的警告,但仍然没有正确恢复 IRQL 级别:

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.

我使用缓冲区并且我没有在任何地方检查错误,所以我认为我锁定正确。在这种情况下,有人知道如何安抚禁食前吗?谢谢!

编辑:

这是我使用锁定功能的示例:

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);

}

我锁定缓冲区,删除所有未决请求,解锁缓冲区并完成请求。 Prefast 告诉我我没有正确恢复 IRQL 级别。当我注释掉 lock/clear/unlock/complete 代码时,prefast 又高兴了。

编辑:

我已经升级到VS2015和WDK10+SDK10了。我添加了 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);

}

现在我得到以下信息:

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.

我注意到 _Acquires_lock_ 和 _Requires_lock_not_held_ 没有定义,所以我查看了它们并注意到它们需要定义 _PREFAST_ 才能工作。所以我将 _PREFAST_ 添加到我的预处理器定义中。现在我得到了一堆链接错误,但没有更多的 prefast 错误!

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_

我想我在将项目转换为 VS2015 时搞砸了。所以我创建了标准的 KMDF 驱动程序项目,为您提供了一个很好的构建框架。我在新项目中打开了静态分析并像以前一样定义了_PREFAST_,它也给了我同样的链接错误。

你走的路很好。 实际上,由于 SAL 注释,您可以告诉 prefast 您的函数锁定或解锁对象: Annotating Locking Behavior

您还需要告知 prefast 有关 IRQL 更改的信息: IRQL annotations for drivers

下面是一个示例代码,它使用了基于您的代码的 SAL 注释,它们可能与旧的 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);
}

备注: 你应该在 ppbBuff->slLock 变量上使用注释,而不是在它的所有者上。

更新: 阅读以上文档后我更新了注释的使用,_IRQL_saves_/_IRQL_restores_ 必须用在函数参数上而不是函数上。您可能还需要使用 _IRQL_raises_ 。我已经编辑了上面的代码示例。您还可以删除 _PREFAST_ 定义以避免 link 问题。 _PREFAST_ 由编译器在开始分析时定义。

更新 2: 除了在 _In_ 参数上使用 _IRQL_saves_/_IRQL_restores 注释之外,您还可以在函数上使用 _IRQL_saves_global_(kind, param) / _IRQL_restores_global_(kind, param) 注释。这是更新后的代码示例:

_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);
}