我们开发了基于(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!
.
.
.
}
我们发现第一次调用RemoveEntryList
inSerialKillAllReadsOrWrites
和第二次调用SerialCancelQueued
将删除同一个条目。
我们已经测试过,如果我们标记第一个RemoveEntryList
,它就通过了,不再蓝屏。
RemoveEntryList
但是为什么本机COM即使调用两次删除同一个条目也不会触发BSOD呢?
有人可以帮我理解为什么吗?谢谢。