[Системное программирование, C, Разработка под Windows] Windows Kernel Drivers — Стандартные ошибки – IRQL
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Данная статья нацелена на тех, кто только недавно начал разрабатывать kernel-драйвера под ОС Windows. В 100-ый раз видишь ненавистную надпись IRQL_NOT_LESS_OR_EQUAL и этот грустный смайлик? Тогда прошу пройти под кат.Одной из основных ошибок, которую я и сам совершал, является жонглирование IRQL так, как душе угодно, и неполное понимание внутреннего устройства работы приоритетов потоков в ядре Windows.
К примеру, у вас есть кусок кода, который генерирует какое-либо событие по PID-процесса.
KSPIN_LOCK SharedDataLock;
NTSTATUS SomeFunction(_In_ HANDLE ProcessId)
{
KIRQL OriginalIrql;
NTSTATUS status = STATUS_UNSUCCESSFUL;
// lock shared data access
KeAcquireSpinLock(&SharedDataLock, &OriginalIrql);
// some code, which works with shared data
PVOID data = GetProcessSharedData(ProcessId);
NT_ASSERT("GetProcessSharedData() must always return shared data.", data);
PEPROCESS Process = NULL;
// next we need to get process image name
status = PsLookupProcessByProcessId(
ProcessId,
&Process
);
if ( !NT_SUCCESS( status ) )
{
goto LOCK_RELEASE;
}
PUNICODE_STRING ProcessImage = NULL;
status = GetProcessImagename(Process, &ProcessImage);
if ( !NT_SUCCESS( status ) )
{
goto PROCESS_LINK_DEREF;
}
// generate some event for our log
GenerateEvent( ProcessImage, data );
PROCESS_LINK_DEREF:
ObDereferenceObject(Process);
LOCK_RELEASE: // release lock
KeReleaseSpinLock(&SharedDataLock, OriginalIrql);
return status;
}
Внутри данного кода, используются разделяемые данные, синхронизация которых, обеспечивается спинлоком. Также нам нужно получить имя процесса, чтобы залогировать событие.Уже увидели ошибку в данном фрагменте?PsLookupProcessByProcessId() – требует соблюдения условия: IRQL <= APC_LEVEL.Так что, подобный код будет часто выдавать BSOD с кодом ошибки IRQL_NOT_LESS_OR_EQUAL.
И вот дальше, начинается самое интересное. Первое решение, которое придёт в голову новичкам, будет менять уровень IRQL перед вызовом данной функции так, чтобы условие соблюдалось.То есть, переписать код — вот так:
KSPIN_LOCK SharedDataLock;
VOID SetIrql(_In_ KIRQL Irql)
{
if ( KeGetCurrentIrql() > Irql )
KeLowerIrql(Irql);
else if ( KeGetCurrentIrql() < Irql )
KzRaiseIrql(Irql);
}
NTSTATUS SomeFunction(_In_ HANDLE ProcessId)
{
KIRQL OriginalIrql;
NTSTATUS status = STATUS_UNSUCCESSFUL;
// lock shared data access
KeAcquireSpinLock(&SharedDataLock, &OriginalIrql);
// some code, which works with shared data
PVOID data = GetProcessSharedData(ProcessId);
NT_ASSERT("GetProcessSharedData() must always return shared data.", data);
SetIrql(APC_LEVEL);
PEPROCESS Process = NULL;
// next we need to get process image name
status = PsLookupProcessByProcessId(
ProcessId,
&Process
);
SetIrql(DISPATCH_LEVEL);
if ( !NT_SUCCESS( status ) )
{
goto LOCK_RELEASE;
}
PUNICODE_STRING ProcessImage = NULL;
status = GetProcessImagename(Process, &ProcessImage);
if ( !NT_SUCCESS( status ) )
{
goto PROCESS_LINK_DEREF;
}
// generate some event for our log
GenerateEvent( ProcessImage, data );
PROCESS_LINK_DEREF:
ObDereferenceObject(Process);
LOCK_RELEASE:
// release lock
KeReleaseSpinLock(&SharedDataLock, OriginalIrql);
return status;
}
Вот теперь то, всё работает достаточно стабильно. Но, на самом деле это не так. Данный код только хорошо маскирует проблему, снижая шансы её проявления до минимума, но в 1 из 1000 случаев, она всё же всплывёт, а вы будет рвать на себе волосы, пытаясь понять в чём же ошибка.И тут нужно вспомнить одно из правил написания драйверов, а именно:
«Понижать IRQL можно только в том случае, если вы его собственноручно повышали, и только до его предыдущего значения!»Для примера:Если какой-либо код вызвал вашу функцию на IRQL = APC_LEVEL, то вы не имеете права опустить его ниже данного уровня. Вы можете поднять IRQL до DISPATCH_LEVEL, потом опустить обратно до APC_LEVEL, но не ниже.Таким образом, более приемлемым вариантом кода, будет:
KSPIN_LOCK SharedDataLock;
_IRQL_requires_max_(APC_LEVEL)
NTSTATUS SomeFunction(_In_ HANDLE ProcessId)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
PEPROCESS Process = NULL;
// next we need to get process image name
status = PsLookupProcessByProcessId(
ProcessId,
&Process
);
if ( !NT_SUCCESS( status ) )
{
goto EXIT_ROUTINE;
}
PUNICODE_STRING ProcessImage = NULL;
status = GetProcessImagename(Process, &ProcessImage);
if ( !NT_SUCCESS( status ) )
{
goto PROCESS_LINK_DEREF;
}
// lock shared data access
KeAcquireSpinLock(&SharedDataLock, &OriginalIrql);
// some code, which works with shared data
PVOID data = GetProcessSharedData(ProcessId);
NT_ASSERT("GetProcessSharedData() must always return shared data.", data);
// generate some event for our log
GenerateEvent( ProcessImage, data );
// release lock
KeReleaseSpinLock(&SharedDataLock, OriginalIrql);
PROCESS_LINK_DEREF:
ObDereferenceObject(Process);
EXIT_ROUTINE:
return status;
}
А вспомогательные функции по типу SetIrql() из 2-го примера, в принципе не являются адекватными с точки зрения интерфейса, т.к. при проектировании отдельных методов в вашем драйвере, важно продумывать ограничения накладываемые на предусловия вызова вашей функции.Для описания данных предусловий, удобно использовать аннотации SAL, их список вы можете посмотреть тут: MSDN SAL 2.0Также Microsoft предоставляет небольшой whitepaper(в самом низу статьи) по управлению приоритетами потоков в ядре, и более подробно рассказывает некоторые тонкости по работе с ними:MSDN Managing Hardware PrioritiesЕсли же, вам всё-таки нужно каким-либо образом вызвать какое-либо Api, требующее более низких значений IRQL, то одним из вариантов решения данной проблемы могут стать WorkItem’ы. Но о них, я расскажу уже в другой статье.
===========
Источник:
habr.com
===========
Похожие новости:
- [Управление e-commerce, Контент-маркетинг, Бизнес-модели, Социальные сети и сообщества] Как социальная коммерция захватывает Китай. И почему это неизбежно случится в России
- [Open source, Системное программирование, Реверс-инжиниринг] Linux для macOS M1: что сделала команда Asahi Linux за январь-февраль 2021 (часть 2) (перевод)
- [Научно-популярное] Lego анонсировала выпуск самого большого и детализированного набора (перевод)
- [Java, API] Сбор метрик Spring Boot приложения c помощью Prometheus и Grafana
- Релиз дистрибутива Manjaro Linux 21.0
- [Open source, GitHub, Законодательство в IT, Биографии гиков] Опубликовано открытое письмо в поддержку Столлмана
- [Производство и разработка электроники, Софт, Настольные компьютеры, Будущее здесь] Софт пожирает мир. Закат универсальных CPU
- [Open source, Читальный зал] «Экономика бесплатного»
- [Разработка веб-сайтов, JavaScript, ООП, ReactJS, TypeScript] Еще один подход к построению архитектуры на фронте
- В Chrome 90 утверждено использование HTTPS по умолчанию в адресной строке
Теги для поиска: #_sistemnoe_programmirovanie (Системное программирование), #_c, #_razrabotka_pod_windows (Разработка под Windows), #_wdm, #_wdf, #_driver, #_kernel, #_windows, #_sistemnoe_programmirovanie (
Системное программирование
), #_c, #_razrabotka_pod_windows (
Разработка под Windows
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 06:01
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Данная статья нацелена на тех, кто только недавно начал разрабатывать kernel-драйвера под ОС Windows. В 100-ый раз видишь ненавистную надпись IRQL_NOT_LESS_OR_EQUAL и этот грустный смайлик? Тогда прошу пройти под кат.Одной из основных ошибок, которую я и сам совершал, является жонглирование IRQL так, как душе угодно, и неполное понимание внутреннего устройства работы приоритетов потоков в ядре Windows. К примеру, у вас есть кусок кода, который генерирует какое-либо событие по PID-процесса. KSPIN_LOCK SharedDataLock;
NTSTATUS SomeFunction(_In_ HANDLE ProcessId) { KIRQL OriginalIrql; NTSTATUS status = STATUS_UNSUCCESSFUL; // lock shared data access KeAcquireSpinLock(&SharedDataLock, &OriginalIrql); // some code, which works with shared data PVOID data = GetProcessSharedData(ProcessId); NT_ASSERT("GetProcessSharedData() must always return shared data.", data); PEPROCESS Process = NULL; // next we need to get process image name status = PsLookupProcessByProcessId( ProcessId, &Process ); if ( !NT_SUCCESS( status ) ) { goto LOCK_RELEASE; } PUNICODE_STRING ProcessImage = NULL; status = GetProcessImagename(Process, &ProcessImage); if ( !NT_SUCCESS( status ) ) { goto PROCESS_LINK_DEREF; } // generate some event for our log GenerateEvent( ProcessImage, data ); PROCESS_LINK_DEREF: ObDereferenceObject(Process); LOCK_RELEASE: // release lock KeReleaseSpinLock(&SharedDataLock, OriginalIrql); return status; } И вот дальше, начинается самое интересное. Первое решение, которое придёт в голову новичкам, будет менять уровень IRQL перед вызовом данной функции так, чтобы условие соблюдалось.То есть, переписать код — вот так: KSPIN_LOCK SharedDataLock;
VOID SetIrql(_In_ KIRQL Irql) { if ( KeGetCurrentIrql() > Irql ) KeLowerIrql(Irql); else if ( KeGetCurrentIrql() < Irql ) KzRaiseIrql(Irql); } NTSTATUS SomeFunction(_In_ HANDLE ProcessId) { KIRQL OriginalIrql; NTSTATUS status = STATUS_UNSUCCESSFUL; // lock shared data access KeAcquireSpinLock(&SharedDataLock, &OriginalIrql); // some code, which works with shared data PVOID data = GetProcessSharedData(ProcessId); NT_ASSERT("GetProcessSharedData() must always return shared data.", data); SetIrql(APC_LEVEL); PEPROCESS Process = NULL; // next we need to get process image name status = PsLookupProcessByProcessId( ProcessId, &Process ); SetIrql(DISPATCH_LEVEL); if ( !NT_SUCCESS( status ) ) { goto LOCK_RELEASE; } PUNICODE_STRING ProcessImage = NULL; status = GetProcessImagename(Process, &ProcessImage); if ( !NT_SUCCESS( status ) ) { goto PROCESS_LINK_DEREF; } // generate some event for our log GenerateEvent( ProcessImage, data ); PROCESS_LINK_DEREF: ObDereferenceObject(Process); LOCK_RELEASE: // release lock KeReleaseSpinLock(&SharedDataLock, OriginalIrql); return status; } «Понижать IRQL можно только в том случае, если вы его собственноручно повышали, и только до его предыдущего значения!»Для примера:Если какой-либо код вызвал вашу функцию на IRQL = APC_LEVEL, то вы не имеете права опустить его ниже данного уровня. Вы можете поднять IRQL до DISPATCH_LEVEL, потом опустить обратно до APC_LEVEL, но не ниже.Таким образом, более приемлемым вариантом кода, будет: KSPIN_LOCK SharedDataLock;
_IRQL_requires_max_(APC_LEVEL) NTSTATUS SomeFunction(_In_ HANDLE ProcessId) { NTSTATUS status = STATUS_UNSUCCESSFUL; PEPROCESS Process = NULL; // next we need to get process image name status = PsLookupProcessByProcessId( ProcessId, &Process ); if ( !NT_SUCCESS( status ) ) { goto EXIT_ROUTINE; } PUNICODE_STRING ProcessImage = NULL; status = GetProcessImagename(Process, &ProcessImage); if ( !NT_SUCCESS( status ) ) { goto PROCESS_LINK_DEREF; } // lock shared data access KeAcquireSpinLock(&SharedDataLock, &OriginalIrql); // some code, which works with shared data PVOID data = GetProcessSharedData(ProcessId); NT_ASSERT("GetProcessSharedData() must always return shared data.", data); // generate some event for our log GenerateEvent( ProcessImage, data ); // release lock KeReleaseSpinLock(&SharedDataLock, OriginalIrql); PROCESS_LINK_DEREF: ObDereferenceObject(Process); EXIT_ROUTINE: return status; } =========== Источник: habr.com =========== Похожие новости:
Системное программирование ), #_c, #_razrabotka_pod_windows ( Разработка под Windows ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 06:01
Часовой пояс: UTC + 5