я, может, просто туплю под вечер, но все же: \src\input\kbdclass\kbdclass.c Код (Text): KeyboardClassDequeueReadByFileObject( IN PDEVICE_EXTENSION DeviceExtension, IN PFILE_OBJECT FileObject ) /*++ Routine Description: Dequeues the next available read with a matching FileObject Assumptions: DeviceExtension->SpinLock is already held (so no further sync is required). --*/ { PIRP irp = NULL; PLIST_ENTRY entry; PIO_STACK_LOCATION stack; PDRIVER_CANCEL oldCancelRoutine; KIRQL oldIrql; if (FileObject == NULL) { return KeyboardClassDequeueRead (DeviceExtension); } for (entry = DeviceExtension->ReadQueue.Flink; entry != &DeviceExtension->ReadQueue; entry = entry->Flink) { irp = CONTAINING_RECORD (entry, IRP, Tail.Overlay.ListEntry); stack = IoGetCurrentIrpStackLocation (irp); if (stack->FileObject == FileObject) { RemoveEntryList (entry); oldCancelRoutine = IoSetCancelRoutine (irp, NULL); // // IoCancelIrp() could have just been called on this IRP. // What we're interested in is not whether IoCancelIrp() was called // (ie, nextIrp->Cancel is set), but whether IoCancelIrp() called (or // is about to call) our cancel routine. To check that, check the result // of the test-and-set macro IoSetCancelRoutine. // if (oldCancelRoutine) { // // Cancel routine not called for this IRP. Return this IRP. // return irp; } else { // // This IRP was just cancelled and the cancel routine was (or will // be) called. The cancel routine will complete this IRP as soon as // we drop the spinlock. So don't do anything with the IRP. // // Also, the cancel routine will try to dequeue the IRP, so make the // IRP's listEntry point to itself. // ASSERT (irp->Cancel); InitializeListHead (&irp->Tail.Overlay.ListEntry); } } } return NULL; } насколко я понимаю, зависнет, если IoSetCancelRoutine() хотя бы раз вернет NULL
верно.. IoSetCancelRoutine() вернет NULL в else выполнится InitializeListHead (&irp->Tail.Overlay.ListEntry); поскольку irp = CONTAINING_RECORD (entry, IRP, Tail.Overlay.ListEntry); то предыдущая операция будет эквивалентна InitializeListHead (entry) и что получится на следующей итерации цикла?
Нет. Ты упустил RemoveEntryList(entry). Она отстегнёт текущий ентри и замкнёт DeviceExtension->ReadQueue. При кажущейся простоте двусвязных списков иногда бывает довольно мозгодробильно Советую взять бумажку и нарисовать пошагово, что происходит со списком.
Four-F пока попробую обойтись без бумажки )) значит, так: irp = CONTAINING_RECORD (entry, IRP, Tail.Overlay.ListEntry); следовательно entry==&Tail.Overlay.ListEntry далее попался irp такой, что (stack->FileObject == FileObject) вошли RemoveEntryList (entry); удаляет Tail.Overlay.ListEntry из списка и замыкает этот список, не меняя содержимого полей Flink и Blink самого Tail.Overlay.ListEntry а вот теперь представим себе, что IoSetCancelRoutine (irp, NULL); возвращает NULL смотрим, что дальше ну ассерт ассертом... InitializeListHead (&irp->Tail.Overlay.ListEntry); Код (Text): VOID FORCEINLINE InitializeListHead( IN PLIST_ENTRY ListHead ) { ListHead->Flink = ListHead->Blink = ListHead; } поскольку entry==&Tail.Overlay.ListEntry, то после этого entry->Flink == entry->Blink == entry цикл повторяется операция entry = entry->Flink в for'е entry не изменится условие entry != &DeviceExtension->ReadQueue будет теперь выполняться всегда, потому что entry не меняется irp = CONTAINING_RECORD (entry, IRP, Tail.Overlay.ListEntry); - entry тот же, значит и irp тот же соответственно (stack->FileObject == FileObject) RemoveEntryList (entry); сейчас entry ссылается на самого себя Код (Text): VOID FORCEINLINE RemoveEntryList( IN PLIST_ENTRY Entry ) { PLIST_ENTRY Blink; PLIST_ENTRY Flink; Flink = Entry->Flink; Blink = Entry->Blink; Blink->Flink = Flink; Flink->Blink = Blink; } очевидно, поля entry или каких-либо элементов списка не изменятся IoSetCancelRoutine (irp, NULL); теперь уже гарантированно вернет NULL, поскольку вызывалась для этого же irp в прошлой итерации и далее все по кругу
Да, ты прав. После RemoveEntryList нужно обновить entry вот так. Код (Text): RemoveEntryList( entry ); entry = entry->Blink; Скорее всего тот, кто писАл этот код, скопипастил этот кусок откуда-то, что-то в нём поменял и ку-ку. Хороший пример того, что имея дело с двусвязными списками надо держать ухо в остро. Надо глянуть в последнем ДДК - пофиксили или нет...
гг kbdclass.sys 5.1.2600.1106 (xpsp1.020828-1920) Код (Text): .text:000105DE ; __stdcall KeyboardClassDequeueReadByFileObject(x,x) .text:000105DE _KeyboardClassDequeueReadByFileObject@8 proc near .text:000105DE ; CODE XREF: KeyboardClassCleanupQueue(x,x,x)+28p .text:000105DE .text:000105DE pDeviceExtension= dword ptr 8 .text:000105DE pFileObject = dword ptr 0Ch .text:000105DE .text:000105DE push ebx .text:000105DF mov ebx, [esp+pFileObject] .text:000105E3 test ebx, ebx .text:000105E5 jnz short loc_105F2 .text:000105E7 push [esp+pDeviceExtension] .text:000105EB call _KeyboardClassDequeueRead@4 ; KeyboardClassDequeueRead(x) .text:000105F0 jmp short loc_10631 .text:000105F2 ; --------------------------------------------------------------------------- .text:000105F2 .text:000105F2 loc_105F2: ; CODE XREF: KeyboardClassDequeueReadByFileObject(x,x)+7j .text:000105F2 push esi .text:000105F3 push edi .text:000105F4 mov edi, [esp+8+pDeviceExtension] .text:000105F8 add edi, 70h .text:000105FB mov ecx, [edi] .text:000105FD jmp short loc_10629 .text:000105FF ; --------------------------------------------------------------------------- .text:000105FF .text:000105FF loc_105FF: ; CODE XREF: KeyboardClassDequeueReadByFileObject(x,x)+4Dj .text:000105FF lea eax, [ecx-58h] .text:00010602 mov edx, [eax+60h] .text:00010605 cmp [edx+18h], ebx .text:00010608 jnz short loc_10627 .text:0001060A mov edx, [ecx] .text:0001060C mov esi, [ecx+4] .text:0001060F mov [esi], edx .text:00010611 mov [edx+4], esi .text:00010614 xor edx, edx .text:00010616 lea esi, [eax+38h] .text:00010619 xchg edx, [esi] .text:0001061B test edx, edx .text:0001061D jnz short loc_1062F .text:0001061F add eax, 58h .text:00010622 mov [eax+4], eax .text:00010625 mov [eax], eax .text:00010627 .text:00010627 loc_10627: ; CODE XREF: KeyboardClassDequeueReadByFileObject(x,x)+2Aj .text:00010627 mov ecx, [ecx] .text:00010629 .text:00010629 loc_10629: ; CODE XREF: KeyboardClassDequeueReadByFileObject(x,x)+1Fj .text:00010629 cmp ecx, edi .text:0001062B jnz short loc_105FF .text:0001062D xor eax, eax .text:0001062F .text:0001062F loc_1062F: ; CODE XREF: KeyboardClassDequeueReadByFileObject(x,x)+3Fj .text:0001062F pop edi .text:00010630 pop esi .text:00010631 .text:00010631 loc_10631: ; CODE XREF: KeyboardClassDequeueReadByFileObject(x,x)+12j .text:00010631 pop ebx .text:00010632 retn 8 .text:00010632 _KeyboardClassDequeueReadByFileObject@8 endp
Да вряд ли. Если спросят, скажешь: "Не скажу, а если будете дурацкие вопросы задавать, то багов искать больше не буду, а про этот всем раскажу на всех ваших форумах". ))