IRP_MJ_WRITE 的 IoBuildAsynchronousFsdRequest

IoBuildAsynchronousFsdRequest with IRP_MJ_WRITE

我在磁盘驱动上开发了一个WDM过滤驱动。我想发送一个异步请求将数据写入磁盘。当我删除 WriteDataIRPCompletion 函数中的 writeBuffer 内存时 windows 会崩溃。

我的问题是:如何在不崩溃的情况下安全地释放 writeBuffer 内存?

这是我的发送请求代码:

#pragma PAGEDCODE
NTSTATUS WriteToDeviceRoutine() {
    PMYDRIVER_WRITE_CONTEXT context = (PMYDRIVER_WRITE_CONTEXT)ExAllocatePool(NonPagedPool,sizeof(PMYDRIVER_WRITE_CONTEXT));
    context->writeBuffer = new(NonPagedPool) unsigned char[4096];

    PIRP pNewIrp = IoBuildAsynchronousFsdRequest(IRP_MJ_WRITE,
         pdx->LowerDeviceObject,
         context->writeBuffer,(wroteRecordNodeCount<<SHIFT_BIT),
         &startingOffset,NULL);
   IoSetCompletionRoutine(pNewIrp,WriteDataIRPCompletion,context,TRUE,TRUE,TRUE);
   IoCallDriver(pdx->LowerDeviceObject,pNewIrp);
}

这是我的补全例程代码:

#pragma LOCKEDCODE
NTSTATUS WriteDataIRPCompletion(IN PDEVICE_OBJECT DeviceObject,IN PIRP driverIrp,IN PVOID Context) {
   PMDL mdl,nextMdl;
   KdPrint((" WriteDataIRPCompletion \n"));
   PMYDRIVER_WRITE_CONTEXT writeContext = (PMYDRIVER_WRITE_CONTEXT) Context;
   if(driverIrp->MdlAddress!=NULL){
      for(mdl=driverIrp->MdlAddress;mdl!=NULL;mdl = nextMdl) {
         nextMdl = mdl->Next;
         MmUnlockPages(mdl);
         IoFreeMdl(mdl);
         KdPrint(("mdl clear\n"));
     }
     driverIrp->MdlAddress = NULL;
   }
   delete [] writeContext->writeBuffer;
   if(Context)
      ExFreePool(Context);

    KdPrint(("leave WriteDataIRPCompletion \n"));
    return STATUS_CONTINUE_COMPLETION;
}

我不太了解您正在处理的具体内容,所以这里有一些细节引起了我的注意。

WriteDataIRPCompletion函数中

PMYDRIVER_WRITE_CONTEXT writeContext = (PMYDRIVER_WRITE_CONTEXT) Context;
// ...
delete [] writeContext->writeBuffer;
if(Context)
     ExFreePool(Context);

请注意,您的 writeContext 源自您的 Context 论点。不过,你好像是deleting/freeing两次分配的内存。

ExFreePool函数docs状态:

Specifies the address of the block of pool memory being deallocated.

看来问题可能是 delete [] writeContext->writeBuffer; 行造成的,只需将其删除即可。

就像现在一样,当您调用 ExFreePool但未设置为 NULL,这反过来导致 ExFreePool 接收到现在无效的指针(即非空指针指向已释放内存的指针)在其 Context 参数中,导致崩溃。

WriteToDeviceRoutine函数中

ExFreePool 的文档明确指出它会释放已分配给其他函数的内存,例如 ExAllocatePool 和其他朋友。

但是,您的代码试图分别使用 new/delete 运算符 allocate/deallocate 和 writeContext->writeBuffer。似乎您应该使用 ExAllocatePool 分配内存,然后使用 ExFreePool 解除分配,而不是像那样尝试手动执行操作。

这些函数可能以特定方式组织内存,if/when在 ExFreePool 中不满足此先决条件,它可能会以崩溃告终。


另外,在调用 ExFreePool 之前检查 if(Context) 是否为 null 似乎很奇怪,但在尝试为本地 writeContext 变量进行类型转换之前却没有检查并使用它。

也许您还应该在第一次使用时检查一下?如果 Context 总是 非空,那么在调用 ExFreePool.

之前也可能不需要检查

你在下一行出错

context = ExAllocatePool(NonPagedPool,sizeof(PMYDRIVER_WRITE_CONTEXT));

什么时候必须

context = ExAllocatePool(NonPagedPool,sizeof(MYDRIVER_WRITE_CONTEXT));

不是sizeof(PMYDRIVER_WRITE_CONTEXT)而是sizeof(MYDRIVER_WRITE_CONTEXT)你分配的不是结构而是指向它的指针。

仅当您的 MYDRIVER_WRITE_CONTEXT 包含单个字段 writeBuffer 并且没有更多数据时,这不会产生错误。否则你会覆盖分配的内存(只有 sizeof(PVOID)),这会产生错误

以及关于 IoBuildAsynchronousFsdRequest 的完成。不幸的是文档不是很好。这里满足

Before calling IoFreeIrp, an additional step is required to free the buffer for an IRP built by IoBuildAsynchronousFsdRequest if the following are all true:

The buffer was allocated from system memory pool.

但随后所有注意力都集中在

The Irp->MdlAddress field is non-NULL.

但是我们必须检查 IRP_DEALLOCATE_BUFFER|IRP_BUFFERED_IO,否则我们可能会泄漏 Irp->AssociatedIrp.SystemBuffer。需要下一个代码

if (Irp->Flags & IRP_BUFFERED_IO)
{
    if (Irp->Flags & IRP_INPUT_OPERATION)
    {
        if (!NT_ERROR(Irp->IoStatus.Status) && Irp->IoStatus.Information)
        {
            memcpy( Irp->UserBuffer, Irp->AssociatedIrp.SystemBuffer, Irp->IoStatus.Information );
        }
    }

    if (Irp->Flags & IRP_DEALLOCATE_BUFFER)
    {
        ExFreePool(Irp->AssociatedIrp.SystemBuffer);
        Irp->AssociatedIrp.SystemBuffer = 0;
    }

    Irp->Flags &= ~(IRP_DEALLOCATE_BUFFER|IRP_BUFFERED_IO);
}

并在 之后检查 if (writeContext) 使用 writeContext->writeBuffer 已经毫无意义和无意义。您真的需要在 WriteToDeviceRoutine()

中检查 context != NULL