Здравствуйте! Я уже отправлял данный пост, но в старую тему, туда мало кто посмотрел, поэтому начинаю тему, а старое сообщение удалю. Дошел до очереди IRP. Образовалась небольшая (надеюсь) проблема. Мне надо попытаться (в качестве эксперимента) в одной функции поставить IRP в очередь, а в другой - извлечь и послать ниже по стеку. Делаю так: Глобальные LIST_ENTRY irpQueue;//очередь KSPIN_LOCK irpQueueSpinLock;//блокировка ... В DriverEntry инициализация: InitializeListHead(&irpQueue); KeInitializeSpinLock(&irpQueueSpinLock); ... Ставлю в очередь в процедуре обработки: KIRQL oldIrql; KeAcquireSpinLock(&irpQueueSpinLock, &oldIrql); IoMarkIrpPending(pIrp); ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); InsertTailList(&irpQueue, &pIrp->Tail.Overlay.ListEntry); KeReleaseSpinLock(&irpQueueSpinLock, oldIrql); ... Извлекаю из очереди и посылаю дальше (в другой процедуре): PIRP currentIrp = NULL; PLIST_ENTRY entry; KIRQL oldIrql; KeAcquireSpinLock(&irpQueueSpinLock, &oldIrql); ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); while (! IsListEmpty (&irpQueue)) { entry = RemoveHeadList (&irpQueue); currentIrp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); } KeReleaseSpinLock(&irpQueueSpinLock, oldIrql); IoCopyCurrentIrpStackLocationToNext(currentIrp); status = IoCallDriver(pdx->pLowerDevice,currentIrp); Все посылается устройству, команду оно выполняет, но возникает BSoD. Без обработки очереди все работало нормально. Подскажите, пожалуйста, где ошибка в этом коде. Заранее благодарен за помощь.
Почему бы не засунуть дамп памяти в WinDBG и не посмотреть, где происходит BSOD? Ты уверен, что выполняются условия твоих ASSERT-ов в обоих процедурах? Находятся ли код этих процедур в Non-paged pool? Что делает цикл Код (Text): while (! IsListEmpty (&irpQueue)) { entry = RemoveHeadList (&irpQueue); currentIrp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); } Выглядит так, как будто он удаляет из очереди все IRP и возвращает указатель на последний. Если тебе надо только отфорвардить IRP нижележащему драйверу в стеке и не интересует что дальше будет с этим запросом, то можешь заменить вызов IoCopyCurrentIrpStackLocationToNext() на IoSkipCurrentIrpStackLocation();
Хотел тоже поумничать, но тут Стен и так все сказал, действительно в цикле у тебя должен находиться обработчки.
Вообще-то сказал далеко не все. Архитектура несколько странноватая - не видел никогда, чтобы сразу скопом обрабатывались все запросы в очереди. Плюс, еще надо обмозговать что делать в случае, если IoCallDriver() вернет STATUS_PENDING.
Sten тут ничего особенного, к примеру при сохранении большого файла, все запросы приходится обрабатывать скопом , насчет STATUS_PENDING, его стоит отслеживать если он действительно будет использовать это Код (Text): IoCopyCurrentIrpStackLocationToNext(currentIrp); status = IoCallDriver(pdx->pLowerDevice,currentIrp); в этом случае ему еще придется писать свою роутин-функцию и ждать ответа, и уже после делать комплит рэквест.
<font color="gray][ Sten</font><!--color--><font color="gray]: Выглядит так, как будто он удаляет из очереди все IRP и возвращает указатель на последний. ]</font><!--color--> Именно так оно и есть и все IRP, кроме последнего просто "повисают в воздухе". Хотя это не должно приводить к BSOD, по крайней мере сразу, т.к. если после постановки в очередь, автору IRP возвращен STATUS_PENDING, как оно и должно быть (по вышеприведенному коду этого не видно, кстати), то он (автор) будет ждать. ksu_ant, BSOD'ы разные бывают. Какой код и параметры? А то так решительно ничего не понятно. Посмотри примеры в ДДК и найди отличия
Здравствуйте! Прошу прощения за вынужденное молчание, - у меня были выходные. Что удалось накопать за эти выходные. Приведенный выше код - работает. Оказывается ошибка была дальше по коду и при определенных условиях, не относящихся к сути вопроса. Но, заработала только тестовая версия. Когда я стал переписывать в реальный алгоритм, все стало гораздо интереснее . Тот код, который я привел ранее, выполнялся так (т.к. это тестовый вариант): IRP анализировался в диспетчерской функции, отправлялся в другую функцию, ставился там в очередь, затем, при возврате в дисп. функцию, извлекался из очереди, как выяснилось, не верно (т.к. удаляются все, кроме последнего), и выполнялся. Все это работало. Теперь ситуация следующая: объявления те же, IRP ставится в очередь так: KIRQL oldIrql; KeAcquireSpinLock(&irpQueueSpinLock, &oldIrql); IoMarkIrpPending(pIrp); ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); InsertTailList(&irpQueue, &pIrp->Tail.Overlay.ListEntry); KeReleaseSpinLock(&irpQueueSpinLock, oldIrql); Затем, при поступлении от приложения IRP, содержащего код запрета/разрешения прохождения сигнала, надо взять первый IRP из очереди и применить к нему действие. Я пытаюсь делать это так: Функция извлечения IRP из очереди: PIRP PopIrp() { PIRP currentIrp = NULL; PLIST_ENTRY entry; KIRQL oldIrql; KeAcquireSpinLock(&irpQueueSpinLock, &oldIrql); ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); while (! IsListEmpty (&irpQueue)) { entry = RemoveHeadList (&irpQueue); currentIrp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); } KeReleaseSpinLock(&irpQueueSpinLock, oldIrql); return currentIrp; } Код обработки прохождения запроса: ... case IOCTL_ALLOW: if(IrpCnt>0){ PIRP LocpIrp=NULL; LocpIrp=PopIrp(); IoCopyCurrentIrpStackLocationToNext(LocpIrp); status = IoCallDriver(pdx->pLowerDevice, LocpIrp); IrpCnt--; } pIrp->IoStatus.Status = STATUS_SUCCESS; pIrp->IoStatus.Information = 0; IoCompleteRequest(pIrp,IO_NO_INCREMENT); break; При выполнении этого кода возникает BSoD. Пишет: An attempt was made to write to read-only memory. А где память трогается, - мне не понятно. Иногда, если запретить прохождение сигнала таким кодом: case IOCTL_DENY: PIRP LocpIrp=NULL; ULONG LocalBytes; LocpIrp=PopIrp(); PIO_STACK_LOCATION pIoStack = IoGetCurrentIrpStackLocation(LocpIrp); LocalBytes = pIoStack->Parameters.Write.Length; LocpIrp->IoStatus.Status = STATUS_SUCCESS; LocpIrp->IoStatus.Information =LocalBytes; IoCompleteRequest( LocpIrp, IO_NO_INCREMENT ); status=STATUS_SUCCESS; IrpCnt--; pIrp->IoStatus.Status = STATUS_SUCCESS; pIrp->IoStatus.Information = 0; IoCompleteRequest(pIrp,IO_NO_INCREMENT); break; BSoD с кодом DRIVER_IRQL_NOT_LESS_OR_EQUAL. Помогите, пожалуйста, разобраться, в чем проблема, и как мне сделать правильное извлечение первого IRP из очереди. Я думаю, проблема именно в функции извлечения. Благодарен за помощь всем откликнувшимся.
ksu_ant, этот код Код (Text): PIRP PopIrp() { PIRP currentIrp = NULL; PLIST_ENTRY entry; KIRQL oldIrql; KeAcquireSpinLock(&irpQueueSpinLock, &oldIrql); ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); while (! IsListEmpty (&irpQueue)) { entry = RemoveHeadList (&irpQueue); currentIrp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); } KeReleaseSpinLock(&irpQueueSpinLock, oldIrql); return currentIrp; } совершенно не понятен, зачем создавать очередь, если в итоге обрабатывается только последний IRP? и далее, Код (Text): IoCopyCurrentIrpStackLocationToNext(LocpIrp); status = IoCallDriver(pdx->pLowerDevice, LocpIrp); тоже фигня, писали же выше, как правильно, судя тому что я вижу тебе нужно сделать так: Код (Text): IoSkipCurrentIrpStackLocation(LocpIrp); status = IoCallDriver(pdx->pLowerDevice, LocpIrp); исправь это, а далее по ходу пьесы смотришь ситуацию Удачи.
ksu_ant, этот код ... совершенно не понятен, зачем создавать очередь, если в итоге обрабатывается только последний IRP? Дело в том, что мне необходимо дождаться ответа пользователя на запрос о запрете/разрешении IRP, а если во время ожидания этого ответа произойдет еще несколько фильтруемых ситуаций, то они встанут в очередь, затем пошлем запросы для каждого из этой очереди последовательно, по мере поступления ответов пользователя. Как я понял, мне необходимо убрать цикл while для того, чтобы я последовательно выбирал первые элементы из очереди? Т.е. написать так: PIRP PopIrp() { PIRP currentIrp = NULL; PLIST_ENTRY entry; KIRQL oldIrql; KeAcquireSpinLock(&irpQueueSpinLock, &oldIrql); ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); entry = RemoveHeadList (&irpQueue); currentIrp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); KeReleaseSpinLock(&irpQueueSpinLock, oldIrql); return currentIrp; } И в этом случае, каждый раз будет выбираться только первый запрос из очереди. Я прав? И нужно ли при этом производить инициализацию головы очереди (InitializeListHead(&irpQueue)) каждый раз при выборе элемента из очереди? И если проблема только в копировании стека IRP, то почему тогда возникает BSoD в функции запрета прохождения, где стек не копируется в принципе.
ksu_ant, не буду вдаваться в суть твоего творения, но скажу одно, если ты маркеруешь запросы как пендинг и закидываешь в список и где-то в подзадаче выполняешь удаление из списка отпенденных запросов, то тебе стоит позаботиться о том чтобы эти запросы были корректно обработаны системой, иначе они будут висеть и как результат приведут к краху системы. Конечно же я не спец по данному вопросу, но все же думаю что я тут прав.
Да еще, почему бы тебе в диспетчере не делать все эти выкрутасы, т.е. нужный тебе IRP отработать, а все прочии откланить или завершить на своем уровне, не передавая далешь?
Да еще, почему бы тебе в диспетчере не делать все эти выкрутасы, т.е. нужный тебе IRP отработать, а все прочии откланить или завершить на своем уровне, не передавая далешь? Не совсем понятно. Я, вроде, так и делаю. Анализируются IRP, ненужные - пропускаются, фильтруемые - в очередь до "особого распоряжения", коим является запрос разрешения/запрета прохождения. На своем уровне - не получится, - идет работа с устройством, а оно, при необходимости, должно выполнить команду, следовательно, я должен передать пакет его драйверу.
Как-то все у тебя запутано. Я бы и сам рад был, чтобы было все проще, но, по-моему здесь так не выйдет. Толковых исходников, описывающих такой механизм (перехватываем IRP, отдаем приложению, пользователь принимает решение, приложение возвращает результат, драйвер запрещает/разрешает пропуск пакета, при этом нельзя прекращать мониторинг остальных фильтруемых ситуаций) я не нашел. Так что приходится что-то придумывать. С Вашей помощью
<font color="gray][ ksu_ant</font><!--color--><font color="gray]: А где память трогается, - мне не понятно. ]</font><!--color--> Любой BSOD кроме кода имеет ещё до 4 доп. параметров. Bug Check 0xBE: ATTEMPTED_WRITE_TO_READONLY_MEMORY Bug Check 0xD1: DRIVER_IRQL_NOT_LESS_OR_EQUAL имеют параметр, который указывает на трогаемый адрес. См. ДДК. SoftICE с включённым faults on может сильно помочь. <font color="gray][ ksu_ant</font><!--color--><font color="gray]: И в этом случае, каждый раз будет выбираться только первый запрос из очереди. Я прав? ]</font><!--color--> Да. <font color="gray][ ksu_ant</font><!--color--><font color="gray]: И нужно ли при этом производить инициализацию головы очереди (InitializeListHead(&irpQueue)) каждый раз при выборе элемента из очереди? ]</font><!--color--> Ни в коем случае! Инициализация очереди производится только однин раз при начале работы драйвера. <font color="gray][ ksu_ant</font><!--color--><font color="gray]: И если проблема только в копировании стека IRP, ]</font><!--color--> Копирование стека здесь совершенно не при чём. IoSkipCurrentIrpStackLocation вместо IoCopyCurrentIrpStackLocationToNext - это просто оптимизация. В твоём случае её желательно сделать, но это не обязательно. Я бы создал два примитива: EnqueueIrp и DequeueFirstIrp Код (Text): VOID EnqueueIrp(PIRP i_pIrp) { KIRQL Irql; ASSERT( i_pIrp ); if ( i_pIrp ) { KeAcquireSpinLock( &irpQueueSpinLock, &Irql ); InsertTailList( &irpQueue, &i_pIrp->Tail.Overlay.ListEntry ); KeReleaseSpinLock( &irpQueueSpinLock, Irql ); } } PIRP DequeueFirstIrp() { PIRP r_pIrp = NULL; PLIST_ENTRY pListEntry; KIRQL Irql; KeAcquireSpinLock( &irpQueueSpinLock, &Irql ); if ( !IsListEmpty(&irpQueue) ){ pListEntry = RemoveHeadList( &irpQueue ); r_pIrp = CONTAINING_RECORD( pListEntry, IRP, Tail.Overlay.ListEntry ); } KeReleaseSpinLock( &irpQueueSpinLock, Irql ); return r_pIrp; } Постановка в очередь: Код (Text): IoMarkIrpPending( pIrp ); EnqueueIrp( pIrp ); return STATUS_PENDING; Выборка из очереди: Код (Text): PIRP pIrp = DequeueFirstIrp(); if ( pIrp ) { if ( DENY ) { pIrp->IoStatus.Information = 0; pIrp->IoStatus.Status = STATUS_NO_SUCH_FILE; // или другой подходящий статус IoCompleteRequest( pIrp, IO_NO_INCREMENT); } else { IoSkipCurrentIrpStackLocation( pIrp ); IoCallDriver( pdx->pLowerDevice, pIrp ); } } Это если тебе тупо нужно выбирать только самый первый IRP. В реальной же ситуации тебе наверное придется как-то различать IRP: какие блокировать, какие пропускать. Для этого нужен какой-то ключ: адрес IRP, адрес файлового объекта, просто порядковый номер и т.п. Когда ты шлёш юзеру запрос, то помечаешь его ключом и потом по ключу ищешь в очереди нужный IRP. Если представить, что ключом будет адрес самого IRP, то примитив, дергающий из очереди нужный IRP, будет такой: Код (Text): PIRP DequeueParticularIrp( PIRP i_pIrpToDequeue ) { PIRP r_pIrp = NULL; PIRP pIrp; KIRQL Irql; PLIST_ENTRY pThisEntry; PLIST_ENTRY pNextEntry; PLIST_ENTRY pListHead; ASSERT( i_pIrpToDequeue ); if ( NULL == i_pIrpToDequeue ) return NULL; pListHead = &irpQueue; KeAcquireSpinLock( &irpQueueSpinLock, &Irql ); for ( pThisEntry = pListHead->Flink, pNextEntry = pThisEntry->Flink; pThisEntry != pListHead; pThisEntry = pNextEntry, pNextEntry = pThisEntry->Flink ) { pIrp = CONTAINING_RECORD( pThisEntry, IRP, Tail.Overlay.ListEntry ); if ( pIrp == i_pIrpToDequeue ) { RemoveEntryList( pThisEntry ); r_pIrp = pIrp; } } KeReleaseSpinLock( &irpQueueSpinLock, Irql ); return r_pIrp; } ЗЫ: Тут ещё с отменой париться надо будет, но пока не заморачивайся.
Забыл сказать. Код (Text): PIO_STACK_LOCATION pIoStack = IoGetCurrentIrpStackLocation(LocpIrp); LocalBytes = pIoStack->Parameters.Write.Length; LocpIrp->IoStatus.Status = STATUS_SUCCESS; LocpIrp->IoStatus.Information =LocalBytes; IoCompleteRequest( LocpIrp, IO_NO_INCREMENT ); status=STATUS_SUCCESS; Возможно к BSOD'у это и не имеет отношение, но по-любому неправильно. Операция же не была выполнена. Information д.б. = 0, а статус д.б. статусом ошибки. К тому же надо ещё посмотреть, не заставляет ли это (Information != 0) диспетчер в/в копировать куда-нить что-нить. Тут может быть завязка на контекст процесса.
Спасибо, Four-F, за столь подробные ответы. На одном из форумов мне также говорили про контекст процесса. Попробую переделать процедуры по Вашему совету. Надеюсь, получится.
Я тоже надеюсь, но боюсь, что всё не так просто. Слей с microsoft "Debugging Tools for Windows". Настрой систему на сброс мини крэшдампа Свойства системы -> Загрузка и восстановление. Дампы будут в %SystemRoot%\Minidump. Грузи дамп в WinDbg из "Debugging Tools for Windows" и пости, то что он (WinDbg) говорит сюда в текстовом виде. Так будет проще понять, что происходит. Ещё придется слить символы (по крайней мере для ntoskrnl) и настроить пути к ним.
Доброе утро! Всех с прошедшими праздниками! Блокировка наконец-то заработала! Делаю так: PIRP LocpIrp=PopIrp(); LocpIrp->IoStatus.Information = 0; LocpIrp->IoStatus.Status = STATUS_ACCESS_DENIED; IoCompleteRequest( LocpIrp, IO_NO_INCREMENT ); status=STATUS_SUCCESS;//для запроса Но, к сожалению, пропуск пакета приводит к BSoD'у. Делаю так: PIRP LocpIrp = PopIrp(); IoSkipCurrentIrpStackLocation( LocpIrp ); status = IoCallDriver(pdx->pLowerDevice, LocpIrp); Код функции PopIrp совпадает с описанной Four-F функцией DequeueFirstIrp. Советуемые программы я пока не скачивал (трафика мало :-(), воспользовался WinDbg из DDK 2000 и дамп, сохраненный при BSoD. Привожу то, что было в конце анализа, так как выше было просто перечисление активных драйверов системы: Kernel Debugger connection established for D:\WINDOWS\MEMORY.DMP Kernel Version 2600 Free loaded @ ffffffff804d0000 Bugcheck 0000008e : c0000005 804ec046 f3532b98 00000000 Stopped at an unexpected exception: code=80000003 addr=ffffffff804fc1bb Hard coded breakpoint hit P.S.: Если это не то, что необходимо, я скачаю указанные программы и сделаю анализ с их помощью. И еще - где взять символы для ntoskrnl для WinXP без сервис-паков? А то качать > 140M полного пакета - больно круто
Это уже кое-что. Лить все 140 метров конечно не обязательно. WinDbg можно настроить так, что он сам будет сливать нужные символы. Как - написано в хелпе. Есть способ проще - запусти \SoftICE\SymbolRetriever\symrtrvr.exe, кинь ему на окно ntoskrnl.exe и тыкни нужную кнопку. Символы льются в упакованном виде, так что там совсем немного. Можно ещё внизу нужные галки поставить, тогда символы и в айс вольются. В WinDbg пропиши путь к каталогу с pdb. Последний WinDbg сливать не обязательно. Я могу ошибится, т.к. не знаю какой sp у тя стоит, но похоже баг тут: Код (Text): :0041C028 IopfCallDriver proc . . . :0041C03D mov [edx+60h], eax ; Irp->Tail.Overlay.CurrentStackLocation = irpSp; :0041C040 mov [eax+IO_STACK_LOCATION.DeviceObject], ecx ; irpSp->DeviceObject = DeviceObject; :0041C043 :0041C043 movzx eax, byte ptr [eax] ; IO_STACK_LOCATION.MajorFunction <font color="red]:0041C046 mov esi, [ecx+8] ; pDriverObject = pDeviceObject->DriverObject;</font><!--color--> ; DriverObject->MajorFunction[pIoStack->MajorFunction]( pDeviceObject, pIrp ); :0041C049 push edx :0041C04A push ecx :0041C04B call dword ptr [esi+eax*4+38h] . . . :0041C050 IopfCallDriver endp У тя в pdx->pLowerDevice какая-то лажа, а не указатель на DeviceObject. Войди в айс, набери u 804ec046. Если увидишь строку, которая выделена красным, то значит я прав. Если есть желание, то можешь скинуть сюда или мне на мыло код DriverEntry, AddDevice и процедуры, которые ставят/достают IRP из очереди. Ну или вообще весь код, если не жалко ЗЫ: И потопчись отладчиком по коду.