2013-02-14 93 views
1

我有一个程序分配一个缓冲区,其指针通过自定义IOCTL传递给内核驱动程序。在驱动程序中,我获得一个Mdl并用“MmGetSystemAddressForMdlSafe”锁定用户程序缓冲区的页面,然后使用Mdl填充用户程序缓冲区。为什么在用户程序中动态分配缓冲区会导致内核驱动程序崩溃?

如果在用户程序中缓冲区是普通数组,那么驱动程序总是按照它应该的那样工作。 (WORD缓冲器[256],其中,字是一个无符号短)

如果用户程序缓冲液代替使用新关键字(WORD *buffer = new WORD[256])或malloc的关键字(WORD *buffer=(WORD*) malloc(sizeof(*buffer)*256)))不时我得到一个BSOD分配和错误是“非分页区域中的页面错误“。

为什么?

谢谢!

EDIT(额外的细节):

在驱动程序我使用MmGetSystemAddressForMdlSafe这种方式:

PVOID p_buffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, HighPagePriority);

的Irp是我接收作为第二个参数,当我处理IRP_MJ_DEVICE_CONTROL的MajorFunction一个PIRP。

后,我检查了p_buffer不为空,我用指针来写用户缓冲区:

READ_PORT_BUFFER_USHORT((PUSHORT)(USHORT)current_port.address, (PUSHORT)p_buffer, 256)

IOCTL定义:

,处理 IRP_MJ_DEVICE_CONTROL
#define IOCTL_TEST_READPORT  CTL_CODE(FILE_DEVICE_TEST, \ 
    TEST_IOCTL_INDEX + 0, \ 
    METHOD_OUT_DIRECT,   \ 
    FILE_ANY_ACCESS) 

驱动程序功能:

NTSTATUS TESTDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) 
{ 
PIO_STACK_LOCATION IrpStack; 
ULONG    input_buffer_size; 
ULONG    output_buffer_size; 
ULONG    control_code; 
PVOID    p_buffer; 
NTSTATUS   nt_status; 
struct    port current_port; 

UNREFERENCED_PARAMETER(DeviceObject); 
PAGED_CODE(); 

Irp->IoStatus.Status  = STATUS_SUCCESS; 
Irp->IoStatus.Information = 0; 

IrpStack = IoGetCurrentIrpStackLocation(Irp); 

switch (IrpStack->MajorFunction) 
{ 

case IRP_MJ_DEVICE_CONTROL: 

    control_code = IrpStack->Parameters.DeviceIoControl.IoControlCode; 

    switch (control_code) 
    { 
     case IOCTL_TEST_READPORT: 
      p_buffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, HighPagePriority); 
      input_buffer_size = IrpStack->Parameters.DeviceIoControl.InputBufferLength; 

      if (!p_buffer) 
      { 
       nt_status = STATUS_INSUFFICIENT_RESOURCES; 
       break; 
      } 
      if (input_buffer_size) 
      { 
       memcpy (&current_port, Irp->AssociatedIrp.SystemBuffer, input_buffer_size); 
       switch (current_port.size) 
       { 
       case 1: 
        current_port.value = (ULONG)READ_PORT_UCHAR((PUCHAR)(USHORT)current_port.address); 
        memcpy (p_buffer, &current_port.value, sizeof(current_port.value)); 
        Irp->IoStatus.Information = sizeof(current_port.value); 
        break; 
       case 0xF0: 
        READ_PORT_BUFFER_USHORT((PUSHORT)(USHORT)current_port.address, (PUSHORT)p_buffer, 256); 
        Irp->IoStatus.Information = sizeof(current_port.value); 
        break; 
       case 2: 
        current_port.value = (ULONG)READ_PORT_USHORT((PUSHORT)(USHORT)current_port.address); 
        memcpy (p_buffer, &current_port.value, sizeof(current_port.value)); 
        Irp->IoStatus.Information = sizeof(current_port.value); 
        break; 
       } 
      } 
      else 
       Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; 
       break; 
     case IRP_MJ_CREATE: 
      KdPrint(("IRP_MJ_CREATE")); 
      break; 

     case IRP_MJ_CLOSE: 
      KdPrint(("IRP_MJ_CLOSE")); 
      break; 

     default: 
      Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; 
      break; 
    } 
    break; 
} 
nt_status = Irp->IoStatus.Status; 
IoCompleteRequest (Irp, IO_NO_INCREMENT); 
return nt_status; 
} 

相关案例是case 0xF0:里面case IOCTL_TEST_READPORT:

+1

你能告诉我们你的IOCTL和锁定页面的代码吗?你可能会计算出一些错误的尺寸。什么是导致BSOD和锁定的缓冲区的起始地址的内存地址?这是为了查看第一个元素是否导致蓝屏死机或中间的某处,并查看它是否在某个边界 – Codeguard 2013-02-14 09:11:42

+0

在任何情况下(如果这是您的驱动程序),驱动程序应验证从用户程序传递的地址和缓冲区不触发页面错误。 – sstn 2013-02-14 09:18:16

+0

不好意思,但是你发布的代码并没有显示你在哪里使用过你刚才提到的'malloc'缓冲区。它只显示你正在获得一个MDL的虚拟地址,所以你可以将该指针传递给'READ_PORT_BUFFER_USHORT'。这看起来是正确的,AFAIK。 – 2013-02-14 11:22:34

回答

1

从我的理解你误解了MmGetSystemAddressForMdlSafe的目的。 Per this document here,您可以使用此函数获取由MDL(内存描述符列表)描述的虚拟地址。

使用虚拟:如果驱动程序必须使用虚拟地址来访问MDL描述页面,则必须使用MmGetSystemAddressForMdlSafe

同一文件还表示,这映射这些页进入系统地址空间地址访问由MDL描述的缓冲区,驱动程序调用MmGetSystemAddressForMdlSafe将缓冲区映射到系统空间。

MmGetSystemAddressForMdlSafe: 地图由MDL描述成系统空间中的物理页,并返回一个虚拟地址MDL。返回的虚拟地址可以在任何IRQL和任何进程上下文中使用

如果你看看MmGetSystemAddressForMdlSafeMSDN documentation然后你会看到这下面一行:

的MmGetSystemAddressForMdlSafe宏指定的MDL描述缓冲区返回一个非分页系统空间的虚拟地址。

它说,这个功能是由一个MDL描述的缓冲器返回非分页虚拟地址。

MDL的定义如下:

的存储器描述符列表(MDL)描述的在物理存储器网页的列表。

这是对物理内存中的页面的描述,而不是虚拟内存。由new分配的缓冲区已经有虚拟地址,试图使用MmGetSystemAddressForMdlSafe就是错误的。您应该使用该功能从MDL获取虚拟地址,而不是虚拟地址范围的MDL。现在

,移动到了page fault in non-paged area的解释:

现在,如果你想想看,很可能是你的缓冲区通过new分配或malloc已经处于分页内存区(事实上,看到它是在用户的土地,这是极有可能的),这意味着试图获得一个虚拟地址到这个缓冲区(这已经是错误的,因为它不是一个MDL),将导致页面错误在非分页区,因为内存缓冲区位于分页区域,而您将其映射到内核中的非分页区域,而非分页区域不会导致页面错误。 (很可能与错误的IRQL级别有关)

+0

我对驱动程序只有基本的了解,但我认为物理内存=分页。非分页=当前从物理内存中删除(例如,到页面文件)。 – Codeguard 2013-02-14 09:55:07

+1

@Codeguard:不,更像是这样的:“非页面缓冲池由虚拟内存地址组成,只要相应的内核对象被分配,这些虚拟内存地址保证位于物理内存中。页面缓冲池由虚拟内存组成,可以分页并离开系统。“ [来源](http://msdn.microsoft.com/en-gb/library/windows/desktop/aa965226(v = vs.85).aspx) – 2013-02-14 09:59:43

+0

这就是“锁定”进来的地方。 – Codeguard 2013-02-14 10:00:26

相关问题