Предварительные аннотации для исправления предупреждений об уровне IRQL
Я пишу драйвер устройства для 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_ в мои определения препроцессора. Теперь я получаю кучу ошибок, связанных с компоновкой, но больше никаких ошибок до быстрого!
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_, как и раньше, и он также дает мне те же ошибки компоновки.
1 ответ
Вы на правильном пути. Действительно, вы можете заранее сказать, что ваши функции блокируют или разблокируют объекты благодаря аннотациям SAL: аннотирующее поведение блокировки
Также необходимо заранее сообщить об изменении IRQL: аннотации IRQL для драйверов
Вот пример кода, который использует аннотации 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);
}