对延迟的过程调用使用 __sdv_save_request 和 __sdv_retrieve_request

延迟过程调用 (DPC) 静态驱动程序验证程序 (SDV) 带来挑战,因为很难跟踪框架请求对象。 一个困难是必须从全局指针(通常是从队列上下文或工作项)检索请求。 为了克服这一困难,静态驱动程序验证程序提供了两个函数: __sdv_save_request__sdv_retrieve_request。 这些函数将延迟的请求映射到 SDV 可以跟踪的请求。

__sdv_save_request__sdv_retrieve_request 函数具有以下语法:

__sdv_save_request( request ) 
__sdv_retrieve_request( request ) 

其中 ,request 可以是任何框架请求对象的句柄。

这些函数仅由静态分析工具使用。 编译器会忽略这些函数。

下面的代码示例演示如何使用 __sdv_save_request 和 __sdv_retrieve_request 函数来指导 SDV,以便 SDV 可以映射延迟的请求。 SDV 可以使用此映射来验证 DeferredRequestCompleted 规则。 DeferredRequestCompleted 规则要求代码中显示 __sdv_save_request 和 __sdv_retrieve_request 。 有两个驱动程序属性规则 (AliasWithinDispatchAliasWithinTimerDpc) ,用于查找 是否存在 __sdv_save_request 和 __sdv_retrieve_request 函数。

在下面的代码示例中, 函数 EchoEvtIoRead 是一个 EvtIoRead 事件回调函数,用于将句柄保存到队列上下文区域中的框架请求对象。 函数 EchoEvtTimerFunc 是检索它的 EvtTimerFunc 事件回调函数。

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

下面的代码示例演示 __sdv_retrieve_request 函数如何映射现有请求,以便 SDV 可以跟踪请求的完成情况。

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