在 RemoveEntryList 中出现错误检查 0x139 的 BSOD

BSOD with Bug Check 0x139 in RemoveEntryList

我们开发了基于(WinDDK 6)原生串口驱动的WDM串口驱动

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

程序上的按钮打开时本应用程序会不断调用IRP_MJ_READ,而在关闭程序时未关闭按钮时出现蓝屏

我们已经用 WinDBG 进行了调试,发现根本原因是 RemoveEntryList,Bug 检查代码告诉我们我们已经调用了 RemoveEntryList 两次。参见 Bug check 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!
    .
    .
    .
}

我们发现 SerialKillAllReadsOrWritesRemoveEntryList 的第一次调用和 SerialCancelQueued 中的第二次调用将删除相同的条目。

而且我们测试过,如果我们标记第一个RemoveEntryList,它就通过了,不再是BSOD。

但为什么原生 COM 即使调用 RemoveEntryList 两次以删除相同的条目也不会触发 BSOD?

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

我发现 WDK8.1 中的 RemoveEntryList 与 WDK6 中的不同。 如果我用 WDK6 构建驱动程序,当我们调用 RemoveEntryList 两次时 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);
}