【发布时间】:2013-01-30 00:42:03
【问题描述】:
我有一个分配缓冲区的程序,该缓冲区的指针通过自定义 IOCTL 传递给内核驱动程序。在驱动程序中,我得到一个 Mdl 并使用“MmGetSystemAddressForMdlSafe”锁定用户程序缓冲区的页面,然后使用 Mdl 填充用户程序缓冲区。
如果在用户程序中缓冲区是一个普通数组,驱动程序总是按它应该的方式工作。 (WORD 缓冲区[256],其中 word 是一个无符号短整数)
如果用户程序缓冲区是用新关键字(WORD *buffer = new WORD[256]) 或malloc 关键字(WORD *buffer=(WORD*) malloc(sizeof(*buffer)*256))) 分配的,我不时收到蓝屏,错误是“非分页区域中的页面错误”。
为什么?
谢谢!
编辑(附加细节):
在驱动中我这样使用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 定义:
#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 0xF0:里面case IOCTL_TEST_READPORT:
【问题讨论】:
-
您能否向我们展示您的 IOCTL 和锁定页面的代码?您可能会错误地计算某些尺寸。导致蓝屏的内存地址和锁定缓冲区的起始地址是什么?这是为了查看第一个元素是导致 BSOD 还是在中间某处,并查看它是否在某个边界上
-
在任何情况下(如果这是您的驱动程序),驱动程序应验证从用户程序传递的地址和缓冲区,以免触发页面错误。
-
抱歉,您发布的代码没有显示您之前提到的
malloced 缓冲区的使用位置。它仅表明您正在获取 MDL 的虚拟地址,因此您可以将该指针传递给READ_PORT_BUFFER_USHORT。这看起来是正确的,AFAIK。 -
从对我的回答的评论中获得的新数据看来,您的问题中似乎缺少一些基本信息,以便能够令人满意地了解错误原因。如果您仍然遇到此问题,请提供更多信息。
-
@Tony The Lion 我在用户程序中执行 malloc,就在调用 dll 中的函数之前,我在其中使用 DeviceIoControl 函数和我的 IOCTL 以及指向我刚刚分配的缓冲区的指针(使用METHOD_OUT_DIRECT)。正如我之前所说,如果这个缓冲区是一个普通的数组(例如:WORD buffer[256];),驱动程序永远不会崩溃并且总是正确运行。相反,如果我使用 WORD buffer = new WORD[256]; 声明缓冲区或 WORD *buffer=(WORD) malloc(sizeof(*buffer)*256)),那么我很可能会因为驱动程序崩溃而出现蓝屏错误“非页面错误” -分页区域”
标签: c++ windows memory-management driver kernel