Предварительные аннотации для исправления предупреждений об уровне 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);
}
Другие вопросы по тегам