我有一个程序,它分配一个缓冲区,它的指针通过自定义的IOCTL传递给内核驱动程序。在驱动程序中,我获得一个Mdl并使用"MmGetSystemAddressForMdlSafe“锁定用户程序缓冲区的页面,然后使用Mdl填充用户程序缓冲区。
如果用户程序中的缓冲区是一个普通数组,则驱动程序始终按其应有的方式工作。(WORD buffer256,其中word是无符号短文)
如果用户程序缓冲区分配了新的关键字(WORD *buffer = new WORD[256])或malloc关键字(WORD *buffer=(WORD*) malloc(sizeof(*buffer)*256))),我会不时得到一个BSOD值,错误是"page fault in non paged“。
为什么?
谢谢!
编辑(其他详细信息):
在驱动程序中,我这样使用MmGetSystemAddressForMdlSafe:
PVOID p_buffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, HighPagePriority);
Irp是我在处理IRP_MJ_DEVICE_CONTROL MajorFunction时作为第二个参数接收的PIRP。
在我检查了p_buffer不是null之后,我使用这个指针来写用户缓冲区:
READ_PORT_BUFFER_USHORT((PUSHORT)(USHORT)current_port.address, (PUSHORT)p_buffer, 256)
IOCTL定义:
#define IOCTL_TEST_READPORT CTL_CODE(FILE_DEVICE_TEST, \
TEST_IOCTL_INDEX + 0, \
METHOD_OUT_DIRECT, \
FILE_ANY_ACCESS)处理IRP_MJ_DEVICE_CONTROL的驱动程序函数
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 IOCTL_TEST_READPORT:中的case 0xF0:
发布于 2013-02-14 17:20:21
据我所知,您误解了MmGetSystemAddressForMdlSafe的用途。Per this document here,则使用此函数来获取由MDL (内存描述符列表)描述的虚拟地址。
。如果驱动程序必须使用虚拟地址来访问MDL所描述的页,则它必须使用MmGetSystemAddressForMdlSafe将这些页映射到系统地址空间
同一份文件中也写道:
为了使用虚拟地址访问
描述的缓冲区,驱动程序调用MmGetSystemAddressForMdlSafe将缓冲区映射到系统空间。
MmGetSystemAddressForMdlSafe:将MDL描述的物理页面映射到系统空间,并返回MDL的虚拟地址。返回的虚拟地址可以在任何IRQL和任何进程上下文中使用。
如果你查看MmGetSystemAddressForMdlSafe的MSDN documentation,你会看到下面这一行:
MDL宏返回指定MmGetSystemAddressForMdlSafe描述的缓冲区的非分页系统空间虚拟地址。
它说明此函数返回由描述的缓冲区的非分页虚拟地址。
MDL的定义如下:
内存描述符列表( memory descriptor list )描述physical memory中的页面列表。
这是对物理内存中的页面的描述,而不是虚拟内存中的页面。由new分配的缓冲区将已经有虚拟地址,尝试在其上使用MmGetSystemAddressForMdlSafe是错误的。您应该使用该函数从MDL获取虚拟地址,而不是从虚拟地址范围获取MDL。
现在,让我们来解释一下page fault in non-paged area
现在想想看,您的new或malloc分配的缓冲区很可能已经在一个分页内存区域中(实际上,因为它在用户区域中,所以极有可能),这意味着尝试获取此缓冲区的虚拟地址(这已经是错误的,因为它不是MDL)将在非分页区域中导致分页错误,因为缓冲区的内存在分页区域中,而您将其映射到内核中的非分页区域,而非分页区域不会导致分页错误。(很可能与错误的IRQL级别有关)
https://stackoverflow.com/questions/14871003
复制相似问题