Utilisation de __sdv_save_request et de __sdv_retrieve_request pour les appels de procédure différée

Les appels de procédure différée (DPC) présentent des défis pour le vérificateur de pilote statique (SDV), car il est difficile de suivre l’objet de demande d’infrastructure. Une difficulté est que la requête doit être récupérée à partir d’un pointeur global, généralement à partir du contexte de file d’attente, ou d’un élément de travail. Pour surmonter cette difficulté, static driver verifier fournit deux fonctions : __sdv_save_request et __sdv_retrieve_request. Ces fonctions mappent la demande différée à une demande que SDV peut suivre.

Les fonctions __sdv_save_request et __sdv_retrieve_request ont la syntaxe suivante :

__sdv_save_request( request ) 
__sdv_retrieve_request( request ) 

request peut être un handle pour n’importe quel objet de demande d’infrastructure.

Ces fonctions sont utilisées uniquement par les outils d’analyse statique. Les fonctions sont ignorées par le compilateur.

L’exemple de code suivant montre comment les fonctions __sdv_save_request et __sdv_retrieve_request sont utilisées pour guider SDV, afin que SDV puisse mapper la requête différée. SDV peut utiliser ce mappage pour vérifier la règle DeferredRequestCompleted . La règle DeferredRequestCompleted nécessite que __sdv_save_request et __sdv_retrieve_request apparaissent dans votre code. Il existe deux règles de propriété de pilote (AliasWithinDispatch, AliasWithinTimerDpc) qui recherchent l’existence des fonctions __sdv_save_request et __sdv_retrieve_request .

Dans l’exemple de code suivant, la fonction EchoEvtIoRead est une fonction de rappel d’événement EvtIoRead qui enregistre le handle dans l’objet de demande d’infrastructure dans la zone de contexte de file d’attente. La fonction EchoEvtTimerFunc est une fonction de rappel d’événement EvtTimerFunc qui la récupère.

VOID
EchoEvtIoRead(
 )in WDFQUEUE   Queue,
 __in WDFREQUEST Request,
 __in size_t      Length
    )
{
/* ..................... */
    // Mark the request as cancelable
    WdfRequestMarkCancelable(Request, EchoEvtRequestCancel);
 
 
    // Defer the completion to another thread from the timer DPC and save the handle to the framework request object by using the __sdv_save_request function. 
    queueContext->CurrentRequest = Request;    
 __sdv_save_request(Request);

    queueContext->CurrentStatus  = Status;

    return;
}

L’exemple de code suivant montre comment la fonction __sdv_retrieve_request mappe une requête existante afin que SDV puisse la suivre pour l’achèvement.

VOID
EchoEvtTimerFunc(
    IN WDFTIMER     Timer
    )
{...................................................
    queue = WdfTimerGetParentObject(Timer);
    queueContext = QueueGetContext(queue);

    //
    // The DPC is automatically synchronized to the queue lock,
    // so this prevents race conditions from occurring without explicit driver-managed locking. The __sdv_retrieve_request function is used so that SDV can restore the deferred request in the timer DPC. Because we know that this deferred request is valid (because it has been previously saved), the __analysis_assume function is used to suppress false defects that might otherwise result in this code path.

    //
 __sdv_retrieve_request(queueContext->CurrentRequest);
    Request = queueContext->CurrentRequest;
 __analysis_assume(Request != NULL);
    if( Request != NULL ) {

        //
        // Try to remove cancel status from the request.
        //
        // The request is not completed if it is already canceled
        // because the EchoEvtIoCancel function has run, or is about to run,
        // and we are racing with it. 

        Status = WdfRequestUnmarkCancelable(Request);
// Because we know that the request is not NULL in this code path and that the request is no longer marked cancelable, we can use the __analysis_assume function to suppress the reporting of a false defect. 

 __analysis_assume(Status != STATUS_CANCELLED);
        if( Status != STATUS_CANCELLED ) {

            queueContext->CurrentRequest = NULL;
            Status = queueContext->CurrentStatus;

            KdPrint(("CustomTimerDPC Completing request 0x%p, Status 0x%x \n", Request,Status));

            WdfRequestComplete(Request, Status);
        }
        else {
            KdPrint(("CustomTimerDPC Request 0x%p is STATUS_CANCELLED, not completing\n",
                                Request));
        }
    }

    return;
}