KeRemoveEntryDeviceQueue引发蓝屏的问题?

whatday 2012-01-10 08:28:59
蓝屏后用windbg打开dump 发现KeRemoveEntryDeviceQueue引发蓝屏 不知道怎么解决 大家帮忙看看 谢谢
如果在应用程序中不调用CancelIo 则不会引发蓝屏
这个程序大致情况是这样:应用程序创建2个线程 两个线程函数是一样的 都是readFile 然后read是异步的 read以后就是 CancelIo 然后是等待
驱动程序里边 对IRP进行了队列化

驱动代码如下:
#pragma LOCKEDCODE
VOID MyStartIo( IN PDEVICE_OBJECT DeviceObject,IN PIRP pFistIrp)
{
KdPrint(("Enter MyStartIo\n"));

KIRQL oldirql;
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)
DeviceObject->DeviceExtension;

PKDEVICE_QUEUE_ENTRY device_entry;

//获取cancel自旋锁
IoAcquireCancelSpinLock(&oldirql);

if(pFirstIrp!=pDevExt->pCurrentIRP||pFirstIrp->Cancel)
{
IoReleaseCancelSpinLock(oldirql);
return;
}else
{
IoSetCancelRoutine(pFirstIrp, NULL);
IoReleaseCancelSpinLock(oldirql);
}

PIRP Irp = pFistIrp;
do
{
KEVENT event;
KeInitializeEvent(&event,NotificationEvent,FALSE);

//等3秒
LARGE_INTEGER timeout;
timeout.QuadPart = -3*1000*1000*10;

//定义一个3秒的延时,主要是为了模拟该IRP操作需要大概3秒左右时间
KeWaitForSingleObject(&event,Executive,KernelMode,FALSE,&timeout);

KdPrint(("Complete a irp:%x\n",Irp));
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0; // no bytes xfered
IoCompleteRequest(Irp,IO_NO_INCREMENT);

device_entry=KeRemoveDeviceQueue(&pDevExt->device_queue);
KdPrint(("device_entry:%x\n",device_entry));
if (device_entry==NULL)
{
break;
}

Irp = CONTAINING_RECORD(device_entry, IRP, Tail.Overlay.DeviceQueueEntry);
}while(1);

KdPrint(("Leave MyStartIo\n"));
}

VOID OnCancelIRP(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)
{
KdPrint(("Enter CancelReadIRP\n"));
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;

if(pdx->pCurrentIRP == Irp)
{
//释放Cancel自旋锁
IoReleaseCancelSpinLock(Irp->CancelIrql);

//从IRP队列取出下一个IRP 并将它送给startIO
IoStartNextPacket(DeviceObject, TRUE);

KeLowerIrql(Irp->CancelIrql);
}else
{
//把IRP从队列中删除 但是并不影响IRP 本身
KeRemoveEntryDeviceQueue(&pdx->device_queue, &Irp->Tail.Overlay.DeviceQueueEntry);
//释放Cancel自旋锁
IoReleaseCancelSpinLock(Irp->CancelIrql);
}

//设置完成状态为STATUS_CANCELLED
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information = 0; // bytes xfered
IoCompleteRequest( Irp, IO_NO_INCREMENT );

KdPrint(("Leave CancelReadIRP\n"));
}

NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp)
{
KdPrint(("Enter HelloDDKRead\n"));

PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)
pDevObj->DeviceExtension;

//将IRP设置为挂起
IoMarkIrpPending(pIrp);

IoSetCancelRoutine(pIrp,OnCancelIRP);

KIRQL oldirql;
//提升IRP至DISPATCH_LEVEL
KeRaiseIrql(DISPATCH_LEVEL, &oldirql);

KdPrint(("HelloDDKRead irp :%x\n",pIrp));

KdPrint(("DeviceQueueEntry:%x\n",&pIrp->Tail.Overlay.DeviceQueueEntry));
if (!KeInsertDeviceQueue(&pDevExt->device_queue, &pIrp->Tail.Overlay.DeviceQueueEntry))
MyStartIo(pDevObj,pIrp);

//将IRP降至原来IRQL
KeLowerIrql(oldirql);

KdPrint(("Leave HelloDDKRead\n"));

//返回pending状态
return STATUS_PENDING;
}

#pragma INITCODE
extern "C" NTSTATUS DriverEntry (IN PDRIVER_OBJECT pDriverObject,IN PUNICODE_STRING pRegistryPath)
{
NTSTATUS status;
//设置卸载函数 设置派遣函数 创建驱动设备对象
return status;
}
#pragma PAGEDCODE
VOID HelloDDKUnload (IN PDRIVER_OBJECT pDriverObject)
{ ........}
#pragma PAGEDCODE
NTSTATUS HelloDDKDispatchRoutin(IN PDEVICE_OBJECT pDevObj,IN PIRP pIrp)
{ ........ }


应用代码
_Thread proc uses ebx esi edi,_lParam

.......
invoke CreateEvent,NULL,FALSE,FALSE,NULL
mov stOverlap.hEvent,eax

invoke ReadFile,_lParam,offset szReadBuffer,10,offset dwBytes,offset stOverlap
invoke CancelIo,_lParam
invoke WaitForSingleObject,stOverlap.hEvent,INFINITE;

ret

_Thread endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
start:
.......
invoke CreateThread,NULL,0,offset _Thread,hDevice,0,offset dwThreadId
mov hThread,eax

invoke CreateThread,NULL,0,offset _Thread,hDevice,0,offset dwThreadId
mov dword ptr [hThread+4],eax

invoke WaitForMultipleObjects,2,offset hThread,TRUE,INFINITE

.......
_Ret:
invoke ExitProcess,NULL
end start

...全文
188 8 打赏 收藏 转发到动态 举报
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
Kyu1225 2012-08-21
  • 打赏
  • 举报
回复
The IRQL_NOT_LESS_OR_EQUAL bug check has a value of 0x0000000A. This indicates that Microsoft Windows or a kernel-mode driver accessed paged memory at DISPATCH_LEVEL or above.

windbg里说明你在DISPATCH_LEVEL上访问了PAGED MEMORY(分页内存),If the IRQL > APC_LEVEL, PAGED_CODE() causes the system to ASSERT.分页内存应该在APC_LEVEL之下级别访问。

你看下device_queue申请的内存是那种类型。
eraser2011 2012-01-17
  • 打赏
  • 举报
回复
把等待3秒部分去掉试试!
whatday 2012-01-16
  • 打赏
  • 举报
回复
呼叫 呼叫 呼叫大家帮我解决一下问题吧
whatday 2012-01-12
  • 打赏
  • 举报
回复
大家帮忙看看 谢谢了
whatday 2012-01-12
  • 打赏
  • 举报
回复
1.蓝屏代码如下:

我实在VM虚拟机里边运行的代码

3.这个赋值代码如下:
//对READ IRP 的处理
NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp)
{
KdPrint(("Enter HelloDDKRead\n"));

PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;

//将IRP设置为挂起
IoMarkIrpPending(pIrp);

IoSetCancelRoutine(pIrp, OnCancelIRP);

KIRQL oldIrql;

//提升IRP至DISPATCH_LEVEL
KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);

//设置了当前IRP 意味着进入了队列化
//在取消例程中不能取消该IRP了
pDevExt->pCurrentIRP = pIrp;

KdPrint(("HelloDDKRead irp :%x\n", pIrp));

KdPrint(("DeviceQueueEntry:%x\n", &pIrp->Tail.Overlay.DeviceQueueEntry));
if(!KeInsertDeviceQueue(&pDevExt->device_queue, &pIrp->Tail.Overlay.DeviceQueueEntry))
MyStartIo(pDevObj, pIrp);

//将IRP降至原来的IRQL
KeLowerIrql(oldIrql);

KdPrint(("Leave HelloDDKRead\n"));

//返回pending状态
return STATUS_PENDING;
}


这里我想达到的目的就是执行pDevExt->pCurrentIRP = pIrp;以后 在取消例程中不能取消该IRP了 因为这个IRP 要由startIO来处理了 所以这样一来我觉得2的问题不会发生了 在取消例程 和 startIO队列化例程中 分别作了验证的
在取消例程中的验证是
if(pdx->pCurrentIRP == Irp)
{
//释放Cancel自旋锁
IoReleaseCancelSpinLock(Irp->CancelIrql);

//从IRP队列取出下一个IRP 并将它送给startIO
IoStartNextPacket(DeviceObject, TRUE);

KeLowerIrql(Irp->CancelIrql);
}
这里理解是执行 IoStartNextPacket KeLowerIrql以后 取消例程的流程就结束了吧

在startIO中的验证是
if(pFirstIrp!=pDevExt->pCurrentIRP||pFirstIrp->Cancel)
曹大夯 2012-01-12
  • 打赏
  • 举报
回复
1. 蓝屏代码是什么?请把蓝屏信息完整贴出来。
2. 是不是Irp = CONTAINING_RECORD(device_entry, IRP, Tail.Overlay.DeviceQueueEntry);
返回的Irp已经被Cancel过了?是不是需要加入一些判断保护后,在进入下一次While循环?
3. pDevExt->pCurrentIRP是在哪赋值的?想达到什么目的啊?
whatday 2012-01-11
  • 打赏
  • 举报
回复
谢谢楼上曹大

蓝屏dump调用信息如下:
nt!KeRemoveDeviceQueue+0x2c
HelloDDK!MyStartIo+0xd6
HelloDDK!HelloDDKRead+0x93
nt!IopfCallDriver+0x31
nt!IopSynchronousServiceTail+0x70
nt!NtReadFile+0x55d
nt!KiFastCallEntry+0xfc
WARNING: Frame IP not in any known module. Following frames may be wrong.
0x7c92e514

我的应用程序使用的汇编
应用程序调用CancelIo时候,驱动应该直接就进入了OnCancelIRP吧? 不能这样进入?我一直以为是这样进入的

KeRemoveEntryDeviceQueue(&pdx->device_queue, &Irp->Tail.Overlay.DeviceQueueEntry); 这句话就是把IRP从自定义的队列中删除吧?本来是把IRP队列化了的,删除了应该没有错啊

应用程序调用CancelIo我本想取消readfile的操作 因为在实际应用中如果readfile很久 我就想要取消他
所以调用CancelIo 就它进入OnCancelIRP 在这里边把IRP从队列中删除然后 再STATUS_CANCELLED 从而达到取消这个IRP的效果
曹大夯 2012-01-10
  • 打赏
  • 举报
回复
蓝屏的具体信息是什么?把蓝屏信息完整地贴出来大家看看。

你的应用程序是汇编么?应用程序调用CancelIo时候,驱动会进入那个Dispatch函数?

OnCancelIRP是一个回调函数,在里面不太适合操作太多无关于IRP的动作。另外,在OnCancelIRP中操作
KeRemoveEntryDeviceQueue(&pdx->device_queue, &Irp->Tail.Overlay.DeviceQueueEntry);
时候,有可能会和MyStartIo中的
device_entry=KeRemoveDeviceQueue(&pDevExt->device_queue);
产生Race Condition吧?

21,600

社区成员

发帖
与我相关
我的任务
社区描述
硬件/嵌入开发 驱动开发/核心开发
社区管理员
  • 驱动开发/核心开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧