Виста, BSOD, IRP

Тема в разделе "WASM.NT.KERNEL", создана пользователем DeeoniS, 13 апр 2007.

  1. DeeoniS

    DeeoniS New Member

    Публикаций:
    0
    Регистрация:
    6 авг 2004
    Сообщения:
    132
    Есть драйвер-фильтр логического диска, котороый при перехвате IRP пакета пытается получить имя файла с которым связан этот пакет. Код получения имени файла взят из FailMon'а. Но этот код вызывает бсод, причем только в висте, и причем через непостоянные промежутки времени.

    Путем эксперемента было выясненно что бсод случается когда мой драйвер формирует IRP пакет и отправляет его ниже по стеку. Вот код создания пакета и процедуры завершения:
    Код (Text):
    1. //----------------------------------------------------------------------
    2. //
    3. // FilemonQueryFileComplete
    4. //
    5. // This routine is used to handle I/O completion for our self-generated
    6. // IRP that is used to query a file's name or number.
    7. //
    8. //----------------------------------------------------------------------
    9. NTSTATUS
    10. FilemonQueryFileComplete(
    11.     PDEVICE_OBJECT DeviceObject,
    12.     PIRP Irp,
    13.     PVOID Context
    14.     )
    15. {
    16.     //
    17.     // Copy the status information back into the "user" IOSB.
    18.     //
    19.     *Irp->UserIosb = Irp->IoStatus;
    20.     if( !NT_SUCCESS(Irp->IoStatus.Status) )
    21.     {
    22.         DbgPrint("dtd!FilemonQueryFileComplete:ERROR ON IRP: %x\n", Irp->IoStatus.Status );
    23.     }
    24.    
    25.     //
    26.     // Set the user event - wakes up the mainline code doing this.
    27.     //
    28.     KeSetEvent(Irp->UserEvent, 0, FALSE);
    29.    
    30.     //
    31.     // Free the IRP now that we are done with it.
    32.     //
    33.     IoFreeIrp(Irp);
    34.    
    35.     //
    36.     // We return STATUS_MORE_PROCESSING_REQUIRED because this "magic" return value
    37.     // tells the I/O Manager that additional processing will be done by this driver
    38.     // to the IRP - in fact, it might (as it is in this case) already BE done - and
    39.     // the IRP cannot be completed.
    40.     //
    41.     return STATUS_MORE_PROCESSING_REQUIRED;
    42. }
    43.  
    44. //----------------------------------------------------------------------
    45. //
    46. // FilemonQueryFile
    47. //
    48. // This function retrieves the "standard" information for the
    49. // underlying file system, asking for the filename in particular.
    50. //
    51. //----------------------------------------------------------------------
    52. BOOLEAN
    53. FilemonQueryFile(
    54.     PDEVICE_OBJECT DeviceObject,
    55.     PFILE_OBJECT FileObject,
    56.     FILE_INFORMATION_CLASS FileInformationClass,
    57.     PVOID FileQueryBuffer,
    58.     ULONG FileQueryBufferLength
    59.     )
    60. {
    61.     PIRP irp;
    62.     KEVENT event;
    63.     IO_STATUS_BLOCK IoStatusBlock;
    64.     PIO_STACK_LOCATION ioStackLocation;
    65.  
    66.     DbgPrint("dtd!FilemonQueryFile:Getting file name for %x\n", FileObject);
    67.  
    68.     //
    69.     // Initialize the event
    70.     //
    71.     KeInitializeEvent(&event, SynchronizationEvent, FALSE);
    72.  
    73.     //
    74.     // Allocate an irp for this request.  This could also come from a
    75.     // private pool, for instance.
    76.     //
    77.     irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
    78.     if(!irp)
    79.     {
    80.         //
    81.         // Failure!
    82.         //
    83.         return FALSE;
    84.     }
    85.  
    86.     //
    87.     // Build the IRP's main body
    88.     //  
    89.     irp->AssociatedIrp.SystemBuffer = FileQueryBuffer;
    90.     irp->UserEvent = &event;
    91.     irp->UserIosb = &IoStatusBlock;
    92.     irp->Tail.Overlay.Thread = PsGetCurrentThread();
    93.     irp->Tail.Overlay.OriginalFileObject = FileObject;
    94.     irp->RequestorMode = KernelMode;
    95.     irp->Flags = 0;
    96.  
    97.     //
    98.     // Set up the I/O stack location.
    99.     //
    100.     ioStackLocation = IoGetNextIrpStackLocation(irp);
    101.     ioStackLocation->MajorFunction = IRP_MJ_QUERY_INFORMATION;
    102.     ioStackLocation->DeviceObject = DeviceObject;
    103.     ioStackLocation->FileObject = FileObject;
    104.     ioStackLocation->Parameters.QueryFile.Length = FileQueryBufferLength;
    105.     ioStackLocation->Parameters.QueryFile.FileInformationClass = FileInformationClass;
    106.  
    107.     //
    108.     // Set the completion routine.
    109.     //
    110.     IoSetCompletionRoutine(irp, FilemonQueryFileComplete, 0, TRUE, TRUE, TRUE);
    111.  
    112.     //
    113.     // Send it to the FSD
    114.     //
    115.     (void) IoCallDriver(DeviceObject, irp);
    116.  
    117.     //
    118.     // Wait for the I/O
    119.     //
    120.     KeWaitForSingleObject(&event, Executive, KernelMode, TRUE, 0);
    121.  
    122.     //
    123.     // Done! Note that since our completion routine frees the IRP we cannot
    124.     // touch the IRP now.
    125.     //
    126.     return NT_SUCCESS( IoStatusBlock.Status );
    127. }
    Анализ дампа командой !analyze -v здесь http://filefactory.ru/1176471867/analyze -v.txt. Кстати еще одним побочным действием является подвисание некотрых процессов. Например при попытки перезагрузить машину она долго думает на стадии завершении работы.

    Может кто что посоветует, а то уже голова совсем не соображает
     
  2. @Asterisk@

    @Asterisk@ New Member

    Публикаций:
    0
    Регистрация:
    18 апр 2007
    Сообщения:
    7
    Вообще желательно не создавать пакет ручками, для этого есть рутина (в твоём случае)IoBuildSynchronousFsdRequest. Но раз уж ты решил создать пакет сам, то тебе лучше почитать про флаги IRP пакета. Файловые системы очень любят комплитить пакеты в контексте потока, начавшего IO, поэтому желательно учитывать это и не давать возможность IO Manager-у для этого повода.

    Сразу вижу несколько грубых нарушений:
    1. Ты создал event и сразу же его присвоил в irp->UserEvent = &event; Ты ведь не в юзерспейсе породил пакет?
    2. Ты присваеваешь irp->Tail.Overlay.Thread, но при этом не ассоциируешь IRP с потоком. В данном случае, тебе нужно поставить туда нуль. Иначе IO Manager посчитает, что должен завершить пакет в контексте текущего потока.
    3. Свой евент передавай лучше как указатель в контекст CompletionRoutine
    IoSetCompletionRoutine(irp, FilemonQueryFileComplete, &event, TRUE, TRUE, TRUE);
    4. Освобождай пакет лишь по окончанию ожидания KeWaitXXX, так ты избежишь потенциального бага в случае, если CompletionRoutine вызовется в контексте арбитра на IRQL > APC_LEVEL

    В твоём же BSOD-е видна работа Io Manager-а, который попытался сделать ObDereference евента, который ты передал в пакете (всё потому что ты его указал, да ещё к тому же указал поток, который якобы дожидается завершения IO операции). Поэтому KeWait бахнулась.
     
  3. DeeoniS

    DeeoniS New Member

    Публикаций:
    0
    Регистрация:
    6 авг 2004
    Сообщения:
    132
    @Asterisk@
    Насчет IoBuildSynchronousFsdRequest она вроде не работает с IRP_MJ_QUERY_INFORMATION.

    Переделал, теперь передаю ивент через контекст и не трогаю irp->Tail.Overlay.Thread... эффект тот же - бсод. Меня смущает, то что в стеке функции переодически повторяются, т.е. похоже на рекурсию. Этот код в ХР работает без проблем. Что еще может быть???
     
  4. @Asterisk@

    @Asterisk@ New Member

    Публикаций:
    0
    Регистрация:
    18 апр 2007
    Сообщения:
    7
    Ну незнаю, что у тебя за код там получился.

    Вот попробуй, у меня вроде работало:
    Код (Text):
    1. NTSTATUS
    2. QueryFileInformationCompletion(
    3.       IN PDEVICE_OBJECT DeviceObject,
    4.       IN PIRP Irp,
    5.       IN PKEVENT CompletionEvent)
    6. {
    7.  
    8.     if (Irp->PendingReturned)
    9.     {
    10.         IoMarkIrpPending(Irp);
    11.     }
    12.  
    13.     Irp->UserIosb->Status = Irp->IoStatus.Status;
    14.     Irp->UserIosb->Information = Irp->IoStatus.Information;
    15.  
    16.     KeSetEvent(CompletionEvent, NULL, FALSE);
    17.  
    18.     return STATUS_MORE_PROCESSING_REQUIRED;
    19. }
    20.  
    21. NTSTATUS
    22. QueryFileInformation(
    23.     PFILE_OBJECT FileObject,
    24.     FILE_INFORMATION_CLASS FileInformationClass,
    25.     ULONG FileQueryBufferLength,
    26.     PVOID FileQueryBuffer,
    27.     ULONG *ReturnLength OPTIONAL)
    28. {
    29.     PIRP Irp;
    30.     KEVENT event;
    31.     IO_STATUS_BLOCK IoStatusBlock;
    32.  
    33.     DEVICE_OBJECT DeviceObject = IoGetRelatedDeviceObject(FileObject);
    34.     if (DeviceObject == NULL)
    35.         return STATUS_UNSUCCESSFUL;
    36.  
    37.     //
    38.     // Initialize the event
    39.     //
    40.     KeInitializeEvent(&event, SynchronizationEvent, FALSE);
    41.  
    42.     Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
    43.  
    44.     if (Irp == NULL)
    45.         return STATUS_INSUFFICIENT_RESOURCES;
    46.  
    47.     //
    48.     // Build the IRP's main body
    49.     //  
    50.     Irp->AssociatedIrp.SystemBuffer = FileQueryBuffer;
    51.     Irp->UserIosb = &IoStatusBlock;
    52.     Irp->UserEvent = NULL;
    53.     Irp->Tail.Overlay.Thread = PsGetCurrentThread();
    54.     Irp->Tail.Overlay.OriginalFileObject = FileObject;
    55.     Irp->RequestorMode = KernelMode;
    56.  
    57.     // set flags to NULL, so IoManager do not touch this IRP
    58.     Irp->Flags = NULL;
    59.  
    60.     Irp->Overlay.AsynchronousParameters.UserApcRoutine = NULL;
    61.  
    62.     //
    63.     // Set up the I/O stack location.
    64.     //
    65.     register PIO_STACK_LOCATION ioStackLocation;
    66.  
    67.     ioStackLocation = IoGetNextIrpStackLocation(Irp);
    68.     ioStackLocation->MajorFunction = IRP_MJ_QUERY_INFORMATION;
    69.     ioStackLocation->DeviceObject = DeviceObject;
    70.     ioStackLocation->FileObject = FileObject;
    71.     ioStackLocation->Parameters.QueryFile.Length = FileQueryBufferLength;
    72.     ioStackLocation->Parameters.QueryFile.FileInformationClass = FileInformationClass;
    73.  
    74.     //
    75.     // Set the completion routine.
    76.     //
    77.     IoSetCompletionRoutine(
    78.         Irp,
    79.         reinterpret_cast<PIO_COMPLETION_ROUTINE>(QueryFileInformationCompletion),
    80.         &event,
    81.         TRUE, TRUE, TRUE);
    82.  
    83.     //
    84.     // Send it to lowest device
    85.     //
    86.     NTSTATUS NtStatus = IoCallDriver(DeviceObject, Irp);
    87.  
    88.     //
    89.     // Wait for the I/O only if pending
    90.     //
    91.     if (NtStatus == STATUS_PENDING)
    92.     {
    93.         KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
    94.     }
    95.  
    96.     if (ReturnLength != NULL)
    97.     {
    98.         *ReturnLength = (ULONG)IoStatusBlock.Information;
    99.     }
    100.  
    101.     NtStatus = IoStatusBlock.Status;
    102.  
    103.     IoFreeIrp(Irp);
    104.  
    105.     return NtStatus;
    106. }
    И вот ещё, не забывай, что эту ф-ю можно вызывать лишь в контекте IRP_MJ_CREATE, в других ситуациях при синхронных операциях BSOD гарантирован
     
  5. DeeoniS

    DeeoniS New Member

    Публикаций:
    0
    Регистрация:
    6 авг 2004
    Сообщения:
    132
    @Asterisk@
    вообщем я пока выбрал чуть другой путь, но думаю в будущем к этой проблеме еще вернусь