0

我们开发了基于(WinDDK 6)本机串行COM端口驱动程序的WDM串行端口驱动程序。

但是我们的客户在使用我们的驱动程序时有一个应用程序触发了 BSOD。

此应用程序在程序上的按钮打开时连续调用IRP_MJ_READ,而在程序关闭时没有关闭按钮时发生BSOD。

我们使用 WinDBG 进行了调试,发现根本原因是RemoveEntryList错误检查代码告诉我们已经调用RemoveEntryList了两次。请参阅错误检查 0x139

经过分析,我们的驱动程序和WinDDK的代码看不出有什么区别,但是原生COM1在运行这个应用程序时不会触发BSOD。

相关代码如下:

当程序被关闭时,系统调用SerialKillAllReadsOrWrites会杀死 ReadQueue 中未决的 IRP。

VOID
SerialKillAllReadsOrWrites(
    IN PDEVICE_OBJECT DeviceObject,
    IN PLIST_ENTRY QueueToClean,
    IN PIRP *CurrentOpIrp
    )
{

    KIRQL cancelIrql;
    PDRIVER_CANCEL cancelRoutine;

    IoAcquireCancelSpinLock(&cancelIrql);

    //
    // Clean the list from back to front.
    //
    while (!IsListEmpty(QueueToClean)) {

        PIRP currentLastIrp = CONTAINING_RECORD(
                                  QueueToClean->Blink,
                                  IRP,
                                  Tail.Overlay.ListEntry
                                  );

        RemoveEntryList(QueueToClean->Blink);

        cancelRoutine = currentLastIrp->CancelRoutine;
        currentLastIrp->CancelIrql = cancelIrql;
        currentLastIrp->CancelRoutine = NULL;
        currentLastIrp->Cancel = TRUE;

        cancelRoutine(
            DeviceObject,
            currentLastIrp
            );               // <- call SerialCancelQueued()

        IoAcquireCancelSpinLock(&cancelIrql);

    }

    .
    .
    .
}
VOID
SerialCancelQueued(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp
    )
{

    PSERIAL_DEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
    PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);

    SERIAL_LOCKED_PAGED_CODE();

    Irp->IoStatus.Status = STATUS_CANCELLED;
    Irp->IoStatus.Information = 0;
    RemoveEntryList(&Irp->Tail.Overlay.ListEntry); // <- BSOD happened here!
    .
    .
    .
}

我们发现第一次调用RemoveEntryListinSerialKillAllReadsOrWrites和第二次调用SerialCancelQueued将删除同一个条目。

我们已经测试过,如果我们标记第一个RemoveEntryList,它就通过了,不再蓝屏。

RemoveEntryList但是为什么本机COM即使调用两次删除同一个条目也不会触发BSOD呢?

有人可以帮我理解为什么吗?谢谢。

4

1 回答 1

0

我在 WDK8.1 中发现RemoveEntryList与 WDK6 中的不同。RemoveEntryList如果我通过WDK6构建驱动程序,当我们调用两次时,Windows不会触发BSOD 。但是,如果驱动是用WDK8.1构建的,当我们调用RemoveEntryList两次时,Windows会触发BSOD。所以,如果我们想通过WDK8.1构建驱动程序,也许SerialKillAllReadsOrWrites应该修改原始代码以避免调用两次。RemoveEntryList

// WDK6:
FORCEINLINE
BOOLEAN
RemoveEntryList(
    _In_ PLIST_ENTRY Entry
    )

{

    PLIST_ENTRY Blink;
    PLIST_ENTRY Flink;

    Flink = Entry->Flink;
    Blink = Entry->Blink;
    Blink->Flink = Flink;
    Flink->Blink = Blink;
    return (BOOLEAN)(Flink == Blink);
}

// WDK 8.1
FORCEINLINE
BOOLEAN
RemoveEntryList(
    _In_ PLIST_ENTRY Entry
    )

{

    PLIST_ENTRY PrevEntry;
    PLIST_ENTRY NextEntry;

    NextEntry = Entry->Flink;
    PrevEntry = Entry->Blink;
    if ((NextEntry->Blink != Entry) || (PrevEntry->Flink != Entry)) {
        FatalListEntryError((PVOID)PrevEntry,
                            (PVOID)Entry,
                            (PVOID)NextEntry);
    }

    PrevEntry->Flink = NextEntry;
    NextEntry->Blink = PrevEntry;
    return (BOOLEAN)(PrevEntry == NextEntry);
}
于 2017-07-26T11:03:57.997 回答