Есть драйвер-фильтр логического диска, котороый при перехвате IRP пакета пытается получить имя файла с которым связан этот пакет. Код получения имени файла взят из FailMon'а. Но этот код вызывает бсод, причем только в висте, и причем через непостоянные промежутки времени. Путем эксперемента было выясненно что бсод случается когда мой драйвер формирует IRP пакет и отправляет его ниже по стеку. Вот код создания пакета и процедуры завершения: Код (Text): //---------------------------------------------------------------------- // // FilemonQueryFileComplete // // This routine is used to handle I/O completion for our self-generated // IRP that is used to query a file's name or number. // //---------------------------------------------------------------------- NTSTATUS FilemonQueryFileComplete( PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context ) { // // Copy the status information back into the "user" IOSB. // *Irp->UserIosb = Irp->IoStatus; if( !NT_SUCCESS(Irp->IoStatus.Status) ) { DbgPrint("dtd!FilemonQueryFileComplete:ERROR ON IRP: %x\n", Irp->IoStatus.Status ); } // // Set the user event - wakes up the mainline code doing this. // KeSetEvent(Irp->UserEvent, 0, FALSE); // // Free the IRP now that we are done with it. // IoFreeIrp(Irp); // // We return STATUS_MORE_PROCESSING_REQUIRED because this "magic" return value // tells the I/O Manager that additional processing will be done by this driver // to the IRP - in fact, it might (as it is in this case) already BE done - and // the IRP cannot be completed. // return STATUS_MORE_PROCESSING_REQUIRED; } //---------------------------------------------------------------------- // // FilemonQueryFile // // This function retrieves the "standard" information for the // underlying file system, asking for the filename in particular. // //---------------------------------------------------------------------- BOOLEAN FilemonQueryFile( PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, FILE_INFORMATION_CLASS FileInformationClass, PVOID FileQueryBuffer, ULONG FileQueryBufferLength ) { PIRP irp; KEVENT event; IO_STATUS_BLOCK IoStatusBlock; PIO_STACK_LOCATION ioStackLocation; DbgPrint("dtd!FilemonQueryFile:Getting file name for %x\n", FileObject); // // Initialize the event // KeInitializeEvent(&event, SynchronizationEvent, FALSE); // // Allocate an irp for this request. This could also come from a // private pool, for instance. // irp = IoAllocateIrp(DeviceObject->StackSize, FALSE); if(!irp) { // // Failure! // return FALSE; } // // Build the IRP's main body // irp->AssociatedIrp.SystemBuffer = FileQueryBuffer; irp->UserEvent = &event; irp->UserIosb = &IoStatusBlock; irp->Tail.Overlay.Thread = PsGetCurrentThread(); irp->Tail.Overlay.OriginalFileObject = FileObject; irp->RequestorMode = KernelMode; irp->Flags = 0; // // Set up the I/O stack location. // ioStackLocation = IoGetNextIrpStackLocation(irp); ioStackLocation->MajorFunction = IRP_MJ_QUERY_INFORMATION; ioStackLocation->DeviceObject = DeviceObject; ioStackLocation->FileObject = FileObject; ioStackLocation->Parameters.QueryFile.Length = FileQueryBufferLength; ioStackLocation->Parameters.QueryFile.FileInformationClass = FileInformationClass; // // Set the completion routine. // IoSetCompletionRoutine(irp, FilemonQueryFileComplete, 0, TRUE, TRUE, TRUE); // // Send it to the FSD // (void) IoCallDriver(DeviceObject, irp); // // Wait for the I/O // KeWaitForSingleObject(&event, Executive, KernelMode, TRUE, 0); // // Done! Note that since our completion routine frees the IRP we cannot // touch the IRP now. // return NT_SUCCESS( IoStatusBlock.Status ); } Анализ дампа командой !analyze -v здесь http://filefactory.ru/1176471867/analyze -v.txt. Кстати еще одним побочным действием является подвисание некотрых процессов. Например при попытки перезагрузить машину она долго думает на стадии завершении работы. Может кто что посоветует, а то уже голова совсем не соображает
Вообще желательно не создавать пакет ручками, для этого есть рутина (в твоём случае)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 бахнулась.
@Asterisk@ Насчет IoBuildSynchronousFsdRequest она вроде не работает с IRP_MJ_QUERY_INFORMATION. Переделал, теперь передаю ивент через контекст и не трогаю irp->Tail.Overlay.Thread... эффект тот же - бсод. Меня смущает, то что в стеке функции переодически повторяются, т.е. похоже на рекурсию. Этот код в ХР работает без проблем. Что еще может быть???
Ну незнаю, что у тебя за код там получился. Вот попробуй, у меня вроде работало: Код (Text): NTSTATUS QueryFileInformationCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PKEVENT CompletionEvent) { if (Irp->PendingReturned) { IoMarkIrpPending(Irp); } Irp->UserIosb->Status = Irp->IoStatus.Status; Irp->UserIosb->Information = Irp->IoStatus.Information; KeSetEvent(CompletionEvent, NULL, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS QueryFileInformation( PFILE_OBJECT FileObject, FILE_INFORMATION_CLASS FileInformationClass, ULONG FileQueryBufferLength, PVOID FileQueryBuffer, ULONG *ReturnLength OPTIONAL) { PIRP Irp; KEVENT event; IO_STATUS_BLOCK IoStatusBlock; DEVICE_OBJECT DeviceObject = IoGetRelatedDeviceObject(FileObject); if (DeviceObject == NULL) return STATUS_UNSUCCESSFUL; // // Initialize the event // KeInitializeEvent(&event, SynchronizationEvent, FALSE); Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE); if (Irp == NULL) return STATUS_INSUFFICIENT_RESOURCES; // // Build the IRP's main body // Irp->AssociatedIrp.SystemBuffer = FileQueryBuffer; Irp->UserIosb = &IoStatusBlock; Irp->UserEvent = NULL; Irp->Tail.Overlay.Thread = PsGetCurrentThread(); Irp->Tail.Overlay.OriginalFileObject = FileObject; Irp->RequestorMode = KernelMode; // set flags to NULL, so IoManager do not touch this IRP Irp->Flags = NULL; Irp->Overlay.AsynchronousParameters.UserApcRoutine = NULL; // // Set up the I/O stack location. // register PIO_STACK_LOCATION ioStackLocation; ioStackLocation = IoGetNextIrpStackLocation(Irp); ioStackLocation->MajorFunction = IRP_MJ_QUERY_INFORMATION; ioStackLocation->DeviceObject = DeviceObject; ioStackLocation->FileObject = FileObject; ioStackLocation->Parameters.QueryFile.Length = FileQueryBufferLength; ioStackLocation->Parameters.QueryFile.FileInformationClass = FileInformationClass; // // Set the completion routine. // IoSetCompletionRoutine( Irp, reinterpret_cast<PIO_COMPLETION_ROUTINE>(QueryFileInformationCompletion), &event, TRUE, TRUE, TRUE); // // Send it to lowest device // NTSTATUS NtStatus = IoCallDriver(DeviceObject, Irp); // // Wait for the I/O only if pending // if (NtStatus == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); } if (ReturnLength != NULL) { *ReturnLength = (ULONG)IoStatusBlock.Information; } NtStatus = IoStatusBlock.Status; IoFreeIrp(Irp); return NtStatus; } И вот ещё, не забывай, что эту ф-ю можно вызывать лишь в контекте IRP_MJ_CREATE, в других ситуациях при синхронных операциях BSOD гарантирован