Мое почтение всем. Пишу WDM драйвер. Один из обработчиков IRP_MJ_DEVICE_CONTROL вызывает BSoD MULTIPLE_IRP_COMPLETE_REQUESTS. Детали: для IRP ставится completion routine, которая вызывается только один раз (что больше всего меня смущает). BSoD происходит при закрытии приложения в случае, если есть не обработанные IRP. Код (Text): 1: kd> !analyze -v ******************************************************************************* * * * Bugcheck Analysis * * * ******************************************************************************* MULTIPLE_IRP_COMPLETE_REQUESTS (44) A driver has requested that an IRP be completed (IoCompleteRequest()), but the packet has already been completed. This is a tough bug to find because the easiest case, a driver actually attempted to complete its own packet twice, is generally not what happened. Rather, two separate drivers each believe that they own the packet, and each attempts to complete it. The first actually works, and the second fails. Tracking down which drivers in the system actually did this is difficult, generally because the trails of the first driver have been covered by the second. However, the driver stack for the current request can be found by examining the DeviceObject fields in each of the stack locations. Arguments: Arg1: 86414720, Address of the IRP Arg2: 00001bbe Arg3: 00000000 Arg4: 00000000 Debugging Details: ------------------ ************************************************************************* *** *** *** *** *** Your debugger is not using the correct symbols *** *** *** *** In order for this command to work properly, your symbol path *** *** must point to .pdb files that have full type information. *** *** *** *** Certain .pdb files (such as the public OS symbols) do not *** *** contain the required information. Contact the group that *** *** provided you with these symbols if you need this command to *** *** work. *** *** *** *** Type referenced: kernel32!pNlsUserInfo *** *** *** ************************************************************************* ************************************************************************* *** *** *** *** *** Your debugger is not using the correct symbols *** *** *** *** In order for this command to work properly, your symbol path *** *** must point to .pdb files that have full type information. *** *** *** *** Certain .pdb files (such as the public OS symbols) do not *** *** contain the required information. Contact the group that *** *** provided you with these symbols if you need this command to *** *** work. *** *** *** *** Type referenced: kernel32!pNlsUserInfo *** *** *** ************************************************************************* IRP_ADDRESS: 86414720 DEFAULT_BUCKET_ID: DRIVER_FAULT BUGCHECK_STR: 0x44 PROCESS_NAME: ScannerClient.e LAST_CONTROL_TRANSFER: from 804f8afd to 8052a5d8 STACK_TEXT: ee7436c8 804f8afd 00000003 ee743a24 00000000 nt!RtlpBreakWithStatusInstruction ee743714 804f96e8 00000003 864a53d0 86414720 nt!KiBugCheckDebugBreak+0x19 ee743af4 804f9c37 00000044 86414720 00001bbe nt!KeBugCheck2+0x574 ee743b14 804ef1e6 00000044 86414720 00001bbe nt!KeBugCheckEx+0x1b ee743b3c 804f4a03 86414720 86414760 8667ca50 nt!IopFreeIrp+0x22 ee743b90 804febb5 86414760 ee743bdc ee743bd0 nt!IopCompleteRequest+0x1b7 ee743be0 80502b35 00000000 00000000 00000000 nt!KiDeliverApc+0xb3 ee743bf8 804fa737 00000103 864a542c 00000000 nt!KiSwapThread+0x89 ee743c24 8057c97a 00000000 00000000 ee743c40 nt!KeDelayExecutionThread+0x1c9 ee743c48 8057e709 014a542c 86414720 864147fc nt!IopCancelAlertedRequest+0x52 ee743c64 8057f4eb 864a5478 00000103 864a53d0 nt!IopSynchronousServiceTail+0xe1 ee743d00 8057804e 0000001c 00000000 00000000 nt!IopXxxControlFile+0x5c5 ee743d34 8054060c 0000001c 00000000 00000000 nt!NtDeviceIoControlFile+0x2a ee743d34 7c90eb94 0000001c 00000000 00000000 nt!KiFastCallEntry+0xfc 0012f9f0 7c90d8ef 7c801671 0000001c 00000000 ntdll!KiFastSystemCallRet 0012f9f4 7c801671 0000001c 00000000 00000000 ntdll!ZwDeviceIoControlFile+0xc 0012fa54 004305a6 0000001c 00222016 0012fb4c kernel32!DeviceIoControl+0xdd 0012ff6c 0042da47 00000001 00332fc0 00333030 ScannerClient!main+0x86 [d:\src\c\scannerclient\scannerclient.c @ 36] 0012ffb8 0042d91f 0012fff0 7c816d4f 7c910738 ScannerClient!__tmainCRTStartup+0x117 [f:\dd\vctools\crt_bld\self_x86\crt\src\crt0.c @ 266] 0012ffc0 7c816d4f 7c910738 ffffffff 7ffd5000 ScannerClient!mainCRTStartup+0xf [f:\dd\vctools\crt_bld\self_x86\crt\src\crt0.c @ 182] 0012fff0 00000000 0042bb72 00000000 78746341 kernel32!BaseProcessStart+0x23 STACK_COMMAND: kb FOLLOWUP_IP: nt!KiDeliverApc+b3 804febb5 8d55d8 lea edx,[ebp-28h] SYMBOL_STACK_INDEX: 6 SYMBOL_NAME: nt!KiDeliverApc+b3 FOLLOWUP_NAME: MachineOwner MODULE_NAME: nt IMAGE_NAME: ntkrpamp.exe DEBUG_FLR_IMAGE_TIMESTAMP: 41107b0d FAILURE_BUCKET_ID: 0x44_nt!KiDeliverApc+b3 BUCKET_ID: 0x44_nt!KiDeliverApc+b3 Followup: MachineOwner --------- Кто-нибудь может объяснить отчего такое может происходить? Особенно меня удручает, что completion routine вызывается только один раз. Заранее благодарен.
Сокету посылаешь DevIoControl ? Винда XP. А вообще это на баг смахивает в винде. По идее уронить ядро обычным приложением - это DoS (если у тя там не стоят свои драйвера и тп).
Да, конечно. ВОт так завершается: Код (Text): NTSTATUS InterruptEventCompletionRoutine(PDEVICE_OBJECT pDeviceObject, PIRP pIrp, PVOID pContext) { KIRQL Irql; PUSB_COMPLETION_INFO pUsbCompletionInfo; PURB pUrb; DbgPrint("InterruptEventCompletionRoutine\n"); pUsbCompletionInfo = (PUSB_COMPLETION_INFO)pContext; if (pIrp ->PendingReturned) { IoMarkIrpPending(pIrp); } if (InterlockedExchangePointer(pUsbCompletionInfo ->ListEntryIrp.pIrp, NULL) || InterlockedExchange(&pUsbCompletionInfo ->ListEntryIrp.CancelFlag, 1)) { pIrp ->IoStatus.Information = pUsbCompletionInfo ->pUrb ->UrbBulkOrInterruptTransfer.TransferBufferLength; CleanupUsbCompletionInfo(pDeviceObject, pUsbCompletionInfo); return STATUS_SUCCESS; } return STATUS_MORE_PROCESSING_REQUIRED; } Вот фрагмент вызова: Код (Text): pStackLocation = IoGetNextIrpStackLocation(pIrp); pStackLocation ->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; pStackLocation ->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB; pStackLocation ->Parameters.Others.Argument1 = pUrb; pDeviceExtension = (PDEVICE_EXTENSION)pDeviceObject ->DeviceExtension; if (pCompletionRoutine) { IoSetCompletionRoutine(pIrp, pCompletionRoutine, pContext, TRUE, TRUE, TRUE); status = IoAcquireRemoveLock(&pDeviceExtension ->RemoveLock, pIrp); if (!NT_SUCCESS(status)) { break; } status = IoCallDriver(pDeviceExtension ->pTopOfStack, pIrp); IoReleaseRemoveLock(&pDeviceExtension ->RemoveLock, pIrp); }
Вопросы: - Ты где-нибудь освобождаешь пришедший IRP самостоятельно? - Ты ставишь функцию отмены перед тем, как поставить IRP в очередь?
Mika0x65 попробуй IoReleaseRemoveLock(&pDeviceExtension ->RemoveLock, pIrp); делать в Completion Routines твоей. перед return STATUS_SUCCESS.
x64 1. Нет, это не мой IRP, я не освобождаю его. 2. Нет, не ставлю. Нет смысла, т.к. я получаю IRP, добавляю его в свою очередь (чтобы можно было отменить) и сразу отправляю IRP низлежащему устройству.
Mika0x65 Ну обычно если это не твой пакет и его надо передать ниже. то ты его передаешь перед этим подняв лок. Потом в комплетион рутине, ты лок снимаешь, обеспечив тем самым то, что приложение не будет выгружено, до того как с IRP выполнятся все действия.
Я, наверное, чего-то не догоняю, но каким образом лок удаления может предотвратить завершение процесса? Они как бы для другого предназначены. К слову, процесс будет уничтожен в любом случае, даже если у его потоков висят незавершённые запросы.
А как же синхронизация с механизмом отмены? Каким образом ты собрался "отменять" запрос, если он уже ушёл ниже?
x64 Нет, драйверов нет. Для отмены я использую механизм, предложенный Они, он позволяет отменять полученные сверху IRP, отосланные низлежащему драйверу.
Черт, я полдня потратил на поиски ошибки. А произошло всего-лишь следующее: if (InterlockedExchangePointer(&pUsbCompletionInfo ->ListEntryIrp.pIrp, NULL) || InterlockedExchange(&pUsbCompletionInfo ->ListEntryIrp.CancelFlag, 1)) { pIrp ->IoStatus.Information = pUsbCompletionInfo ->pUrb ->UrbBulkOrInterruptTransfer.TransferBufferLength; CleanupUsbCompletionInfo(pDeviceObject, pUsbCompletionInfo); return STATUS_SUCCESS; } Не хватало амперсанда, а т.к. ф-ия принимает PVOID, то контроль типов не предупредил. Соответственно, повреждался _IRP::Type, а IopFreeIrp его проверяет, и если он 0, то вызывает KeBugCheck(44, ...). Короче, я сам повредил. Велик отладчик, дизассемблер и аппаратные точки останова!