2013-02-14 82 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

相關問題