我正在为Windows 7 32位编写设备驱动程序。我正在使用WDK版本7600.16385.1。到目前为止,一切进展顺利,但是prefast一直告诉我我搞砸了IRQL级别。特别是当我尝试锁定/解锁共享缓冲区时。
我有一个表示缓冲区的结构,如下所示:
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级别。当我注释掉锁定/清除/解锁/完成代码时,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批注,您可以快速告知您的函数可以锁定或解锁对象:
Annotating Locking Behavior
您还需要提前告知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_
定义以避免链接问题。 _PREFAST_
由编译器在开始分析时定义。更新2:除了在
_IRQL_saves_/_IRQL_restores
参数上使用_In_
批注外,您还可以在函数上使用_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);
}
关于c - 预快速批注可修复IRQL级警告,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/37266215/