We have developed a WDM serial port driver which is based on (WinDDK 6) native serial COM port driver.
But our customer has a application triggering BSOD when using our driver.
This application calls IRP_MJ_READ continuously when the button on the program is turned on, and BSOD happened when program is being closed without turning off the button.
We have debugged with WinDBG and found the root cause is RemoveEntryList
and the Bug check code tells us we have called RemoveEntryList
twice. See Bug check 0x139.
After analyzing, we can't see differences for the codes between our driver and WinDDK, but native COM1 does not trigger BSOD when runing this application.
The related codes are as followings:
When the program is being closed, the system call SerialKillAllReadsOrWrites
to kill the pending IRPs in the ReadQueue.
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!
.
.
.
}
We found the first call of RemoveEntryList
in SerialKillAllReadsOrWrites
and the second call in SerialCancelQueued
are going to remove the same entry.
And we have tested that if we mark the first RemoveEntryList
, it passed, no longer BSOD.
But why native COM doesn't trigger BSOD even for calling RemoveEntryList
twice to remove the same entry?
Could someone help me to understand why? Thanks.
I found RemoveEntryList
in WDK8.1 is different in WDK6.
If I build driver by WDK6, Windows will not trigger BSOD when we call RemoveEntryList
twice.
However, if driver is built by WDK8.1, Windows will trigger BSOD when we call RemoveEntryList
twice.
So, maybe the original codes in SerialKillAllReadsOrWrites
should be modified to avoid calling RemoveEntryList
twice if we want to build driver by WDK8.1.
// 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);
}