为什么在用户程序中动态分配缓冲区会导致内核驱动程序崩溃?
我有一个程序分配一个缓冲区,其指针通过自定义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 (¤t_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, ¤t_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, ¤t_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:
从我的理解你误解了MmGetSystemAddressForMdlSafe
的目的。 Per this document here,您可以使用此函数获取由MDL(内存描述符列表)描述的虚拟地址。
。
使用虚拟:如果驱动程序必须使用虚拟地址来访问MDL描述页面,则必须使用MmGetSystemAddressForMdlSafe
同一文件还表示,这映射这些页进入系统地址空间地址访问由MDL描述的缓冲区,驱动程序调用MmGetSystemAddressForMdlSafe将缓冲区映射到系统空间。
MmGetSystemAddressForMdlSafe: 地图由MDL描述成系统空间中的物理页,并返回一个虚拟地址MDL。返回的虚拟地址可以在任何IRQL和任何进程上下文中使用。
如果你看看MmGetSystemAddressForMdlSafe
的MSDN documentation然后你会看到这下面一行:
的MmGetSystemAddressForMdlSafe宏指定的MDL描述缓冲区返回一个非分页系统空间的虚拟地址。
它说,这个功能是由一个MDL描述的缓冲器返回非分页虚拟地址。
MDL的定义如下:
的存储器描述符列表(MDL)描述的在物理存储器网页的列表。
这是对物理内存中的页面的描述,而不是虚拟内存。由new
分配的缓冲区已经有虚拟地址,试图使用MmGetSystemAddressForMdlSafe
就是错误的。您应该使用该功能从MDL获取虚拟地址,而不是虚拟地址范围的MDL。现在
,移动到了page fault in non-paged area
的解释:
现在,如果你想想看,很可能是你的缓冲区通过new
分配或malloc
已经处于分页内存区(事实上,看到它是在用户的土地,这是极有可能的),这意味着试图获得一个虚拟地址到这个缓冲区(这已经是错误的,因为它不是一个MDL),将导致页面错误在非分页区,因为内存缓冲区位于分页区域,而您将其映射到内核中的非分页区域,而非分页区域不会导致页面错误。 (很可能与错误的IRQL级别有关)
我对驱动程序只有基本的了解,但我认为物理内存=分页。非分页=当前从物理内存中删除(例如,到页面文件)。 – Codeguard 2013-02-14 09:55:07
@Codeguard:不,更像是这样的:“非页面缓冲池由虚拟内存地址组成,只要相应的内核对象被分配,这些虚拟内存地址保证位于物理内存中。页面缓冲池由虚拟内存组成,可以分页并离开系统。“ [来源](http://msdn.microsoft.com/en-gb/library/windows/desktop/aa965226(v = vs.85).aspx) – 2013-02-14 09:59:43
这就是“锁定”进来的地方。 – Codeguard 2013-02-14 10:00:26
你能告诉我们你的IOCTL和锁定页面的代码吗?你可能会计算出一些错误的尺寸。什么是导致BSOD和锁定的缓冲区的起始地址的内存地址?这是为了查看第一个元素是否导致蓝屏死机或中间的某处,并查看它是否在某个边界 – Codeguard 2013-02-14 09:11:42
在任何情况下(如果这是您的驱动程序),驱动程序应验证从用户程序传递的地址和缓冲区不触发页面错误。 – sstn 2013-02-14 09:18:16
不好意思,但是你发布的代码并没有显示你在哪里使用过你刚才提到的'malloc'缓冲区。它只显示你正在获得一个MDL的虚拟地址,所以你可以将该指针传递给'READ_PORT_BUFFER_USHORT'。这看起来是正确的,AFAIK。 – 2013-02-14 11:22:34