Parallel calls to VhfReadReportSubmit lead to Stack Overflow BSOD

110 views Asked by At

I am writing a windows kmdf driver for a virtual HID device using the vhf framework. The device takes any write reports submitted to it, modifies them slightly, and spits them out via VhfReadReportSubmit. Unfortunately, I am getting stack-overflow BSODs when multiple reports come in parallel. I should note that I am relying on the vhf framework to buffer the submitted reports (this is the default) and thus, per documentation, there should be no restrictions on calling the VhfReadReportSubmit function.

The relevant code from AddDevice which calls VhfCreate:

    DeviceCtx* ctx = getCtx(device);
    ctx->parallel_ops_counter = 0;
    

    VHF_CONFIG_INIT(&vhfCfg, WdfDeviceWdmGetDeviceObject(device), sizeof(report_desc), report_desc);
    vhfCfg.VhfClientContext = ctx;
    vhfCfg.OperationContextSize = MAX_HID_REPORT_SIZE;
    vhfCfg.EvtVhfAsyncOperationWriteReport = ForwardReport;
    vhfCfg.VendorID = VENDOR_ID;
    vhfCfg.ProductID = PRODUCT_ID;

    status = VhfCreate(&vhfCfg, &ctx->vhfHandle);

and the ForwardReport function, which gets called when someone sumbits a write report to the device (I've removed some trace messages for simplicity):

// FIXME: Submitting a lot of HID reports in parallel leads to a bugcheck (BSOD)
void ForwardReport(DeviceCtx* devCtx, VHFOPERATIONHANDLE op, void* opCtx, PHID_XFER_PACKET report) {
    UNREFERENCED_PARAMETER(opCtx);
    NTSTATUS status;
    HID_XFER_PACKET packet_copy;

    // Prevent BSOD (STACK_OVERFLOW) which for some unknown reason
    // happens when too many HID reports are submitted in parallel
    if (devCtx->parallel_ops_counter > MAX_PARALLEL_OPS) {
        VhfAsyncOperationComplete(op, STATUS_INSUFFICIENT_RESOURCES); // STATUS_INTERNAL_ERROR
        return;
    }
    
    devCtx->parallel_ops_counter++;

    // Copy the report to a temporary buffer
    if (report->reportBufferLen > MAX_HID_REPORT_SIZE) {
        VhfAsyncOperationComplete(op, STATUS_BUFFER_OVERFLOW);
        return;
    }
    packet_copy.reportBuffer = opCtx;
    packet_copy.reportBufferLen = report->reportBufferLen;
    packet_copy.reportId = report->reportId;
    memcpy(opCtx, report->reportBuffer, report->reportBufferLen);
    
    // Change the report channel
    Packet* pkt = (Packet*)(packet_copy.reportBuffer);
    if (IS_CLIENT_CHANNEL(pkt->channel)) {
        pkt->channel = ENCODE_CLIENT_CHANNEL(pkt->channel);
    } else if(IS_CONTROL_CHANNEL(pkt->channel)) {
        pkt->channel = DECODE_CLIENT_CHANNEL(pkt->channel);
    }
    else if (pkt->channel == BROADCAST_CHANNEL_SERVER) {
        pkt->channel = BROADCAST_CHANNEL_CLIENT;
    }
    else if (pkt->channel == BROADCAST_CHANNEL_CLIENT) {
        pkt->channel = BROADCAST_CHANNEL_SERVER;
    }

    // Submit the changed report
    status = VhfReadReportSubmit(devCtx->vhfHandle, &packet_copy);

    // Set the operation return code
    VhfAsyncOperationComplete(op, status);
    
    devCtx->parallel_ops_counter--;
}

If I remove the MAX_PARALLEL_OPS check, the driver regularly results in a BSOD. Is there anything I am doing wrong?

0

There are 0 answers