Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Все разработчики драйверов должны учитывать уровни запросов прерывания (IRQLs). IRQL — целое число от 0 до 31; PASSIVE_LEVEL, DISPATCH_LEVEL и APC_LEVEL обычно называются символично, а другие — числовыми значениями. Повышение и снижение IRQL должно соответствовать строгой дисциплине стека. Функция должна стремиться вернуться к тому же IRQL, в котором она была вызвана. Значения IRQL должны не уменьшаться в стеке. И функция не может снизить IRQL без первого ее повышения. Аннотации IRQL предназначены для соблюдения этих правил.
Если в коде драйвера есть заметки IRQL, средства анализа кода могут сделать более точным вывод о диапазоне уровней, на которых должна выполняться функция и может более точно находить ошибки. Например, можно добавить заметки, указывающие максимальное значение IRQL, с помощью которого можно вызвать функцию; Если функция вызывается в более высоком irQL, средства анализа кода могут определить несоответствия.
Функции драйвера должны быть аннотированы максимально возможными сведениями об IRQL, которые могут быть подходящими. Если доступна дополнительная информация, она помогает средствам анализа кода при последующей проверке вызывающей функции и вызываемой функции. В некоторых случаях добавление заметки является хорошим способом подавления ложноположительных срабатываний. Некоторые функции, такие как служебная функция, можно вызывать в любой IRQL. В этом случае отсутствие аннотации IRQL является правильной аннотацией.
При аннотации функции для IRQL особенно важно учитывать, как может развиваться функция, а не только ее текущая реализация. Например, функция, реализованная, может корректно функционировать при более высоком IRQL, чем это предполагалось разработчиком. Хотя заманчиво аннотировать функцию на основе того, что на самом деле делает код, дизайнер может знать о будущих требованиях, таких как необходимость снизить максимальное значение IRQL для некоторых будущих улучшений или ожидаемых системных требований. Заметка должна быть получена от намерения конструктора функций, а не от фактической реализации.
Заметки в следующей таблице можно использовать для указания правильного IRQL для функции и его параметров. Значения IRQL определяются в Wdm.h.
| Заметка IRQL | Описание |
|---|---|
| _IRQL_requires_max_(irql) | Irql — это максимальное значение IRQL, с помощью которого можно вызвать функцию. |
| _IRQL_requires_min_(irql) | Irql — это минимальное значение IRQL, с помощью которого можно вызвать функцию. |
| _IRQL_requires_(irql) | Функция должна быть введена на IRQL, указанном irql. |
| _IRQL_raises_(irql) (повышает) | Функция завершает работу на указанном уровне IRQL, но ее можно вызывать только для повышения (не понижения) текущего уровня IRQL. |
| _IRQL_saves_ | Аннотированный параметр сохраняет текущий IRQL для последующего восстановления. |
| _IRQL_restores_ | Аннотированный параметр содержит значение IRQL из IRQL_saves , которое необходимо восстановить при возврате функции. |
| _IRQL_saves_global_(kind, param) | Текущий IRQL сохраняется в расположении, которое является внутренним для средств анализа кода, из которых необходимо восстановить IRQL. Эта аннотация используется для аннотирования функции. Расположение идентифицируется по категории и далее уточняется с помощью параметра. Например, OldIrql может быть типом, а FastMutex может быть параметром, который содержал старое значение IRQL. |
| _IRQL_restores_global_(kind, param) | IRQL, сохраненный функцией, аннотированной с IRQL_saves_global, восстанавливается из расположения, которое является внутренним для инструментов анализа кода. |
| _IRQL_always_function_min_(значение) | Значение IRQL — это минимальное значение, к которому функция может снизить IRQL. |
| _IRQL_always_function_max_(значение) | Максимальное значение IRQL — это наибольшее значение, до которого функция может повысить IRQL. |
| _IRQL_requires_same_ | Аннотированная функция должна войти и выйти на том же уровне IRQL. Функция может изменить IRQL, но она должна восстановить IRQL до исходного значения перед выходом. |
| _IRQL_uses_cancel_ | Аннотированный параметр — это значение IRQL, которое должно быть восстановлено функцией обратного вызова DRIVER_CANCEL. В большинстве случаев вместо этого используйте аннотацию IRQL_is_cancel. |
Аннотации для DRIVER_CANCEL
Существует разница между аннотациями _IRQL_uses_cancel_ и _IRQL_is_cancel_. Примечания _IRQL_uses_cancel_ просто указывает, что аннотированный параметр является значением IRQL, которое должно быть восстановлено функцией обратного вызова DRIVER_CANCEL. Заметка _IRQL_is_cancel_ представляет собой составную заметку, состоящую из _IRQL_uses_cancel_ и нескольких других заметок, которые обеспечивают правильное поведение функции служебной программы обратного вызова DRIVER_CANCEL. Аннотация _IRQL_uses_cancel_ сама по себе полезна лишь иногда; например, если остальные обязательства, описанные в _IRQL_is_cancel_, уже были выполнены каким-либо другим способом.
| Заметка IRQL | Описание |
|---|---|
| _IRQL_is_cancel_ | Аннотированный параметр — это IRQL, переданный в рамках вызова функции обратного вызова DRIVER_CANCEL. Эта аннотация указывает, что функция является утилитарной и вызывается из подпрограмм отмены, и что она выполняет обязательные действия для функций DRIVER_CANCEL, включая выпуск блокировки отмены операции спинлока. |
Взаимодействие заметок IRQL
Заметки параметров IRQL взаимодействуют больше друг с другом, чем с другими заметками, так как значение IRQL задано, сбрасывается, сохраняется и восстанавливается различными вызываемыми функциями.
Указание максимального и минимального IRQL
Аннотации _IRQL_requires_max_ и _IRQL_requires_min_ указывают, что функция не должна вызываться из IRQL, который выше или ниже указанного значения. Например, когда PREfast видит последовательность вызовов функций, которые не изменяют IRQL, если он находит один со значением _IRQL_requires_max_ ниже ближайшего _IRQL_requires_min_, он сообщает предупреждение о втором вызове, который он встречает. Ошибка может произойти в первом вызове; сообщение указывает, где произошла другая половина конфликта.
Если аннотации функции упоминают IRQL и явно не применяется _IRQL_requires_max_, средство анализа кода неявно применяет аннотацию _IRQL_requires_max_(DISPATCH_LEVEL), что, как правило, правильно, за редким исключением. Неявно применение этого значения по умолчанию устраняет множество заметок и делает исключения гораздо более видимыми.
Заметка _IRQL_requires_min_(PASSIVE_LEVEL) всегда подразумевается, так как IRQL не может снизиться; следовательно, нет соответствующего явного правила о минимальном IRQL. Очень мало функций имеют и верхнюю границу, кроме DISPATCH_LEVEL, и нижнюю границу, отличные от PASSIVE_LEVEL.
Некоторые функции вызываются в контексте, в котором вызываемая функция не может безопасно поднять IRQL выше определенного максимума или, более часто, не может безопасно снизить ее ниже некоторого минимума. Аннотации _IRQL_always_function_max_ и _IRQL_always_function_min_ помогают PREfast обнаруживать случаи, когда это происходит непреднамеренно.
Например, функции типа DRIVER_STARTIO аннотированы использованием _IRQL_always_function_min_(DISPATCH_LEVEL). Это означает, что во время выполнения функции DRIVER_STARTIO снижение IRQL ниже DISPATCH_LEVEL является ошибкой. Другие аннотации указывают на то, что функция должна быть инициализирована и завершена на уровне DISPATCH_LEVEL.
Указание явного IRQL
Используйте аннотацию _IRQL_raises_ или _IRQL_requires_, чтобы помочь инструменту PREfast лучше сообщить о несоответствии, обнаруженном с помощью аннотаций _IRQL_requires_max_ или _IRQL_requires_min_, так как в этом случае он будет знать IRQL.
Аннотация _IRQL_raises_ указывает, что функция возвращается с новым уровнем IRQL. При использовании аннотации _IRQL_raises_ она также эффективно устанавливает аннотацию _drv_maxFunctionIRQL на то же значение IRQL. Однако, если функция повышает IRQL выше окончательного значения, а затем снижает его до окончательного значения, следует добавить явную аннотацию _IRQL_always_function_max_ после аннотации _IRQL_raises_, чтобы разрешить более высокое значение IRQL.
Повышение или снижение IRQL
Заметка _IRQL_raises_ указывает, что функция должна использоваться только для повышения IRQL и не должна использоваться для понижения IRQL, даже если синтаксис функции позволит это. KeRaiseIrql является примером функции, которая не должна использоваться для снижения IRQL.
Сохранение и восстановление IRQL
Используйте _IRQL_saves_ и _IRQL_restores_ заметки, чтобы указать, что текущий IRQL (известно ли это точно или только приблизительно) сохраняется или восстанавливается из аннотированного параметра.
Некоторые функции сохраняют и восстанавливают IRQL неявно. Например, системная функция ExAcquireFastMutex сохраняет IRQL в непрозрачном расположении, связанном с быстрым объектом мьютекса, который определяет первый параметр; сохраненный IRQL восстанавливается соответствующей функцией ExReleaseFastMutex для этого быстрого объекта мьютекса. Чтобы явно указать эти действия, используйте аннотации _IRQL_saves_global_ и _IRQL_restores_global_. Параметры типа и param указывают, где сохраняется значение IRQL. Расположение, в котором сохранено значение, не должно быть точно указано, если заметки, которые сохраняют и восстанавливают значение, согласованы.
Поддержание того же уровня IRQL
Чтобы указать, что изменение уровня IRQL ожидается, следует аннотировать функции, создаваемые вашим драйвером, с помощью аннотации _IRQL_requires_same_ или одной из других аннотаций IRQL. В отсутствие заметок, указывающих на любые изменения в IRQL, средства анализа кода будут выдавать предупреждение для любой функции, которая не выходит из той же IRQL, с которой была введена функция. Если изменение IRQL является запланированным, добавьте соответствующую аннотацию для подавления ошибки. Если изменение IRQL не предназначено, код должен быть исправлен.
Сохранение и восстановление IRQL для подпрограмм отмены ввода-вывода
Используйте заметку _IRQL_uses_cancel_, чтобы указать, что аннотированный параметр является значением IRQL, которое должно быть восстановлено функцией обратного вызова DRIVER_CANCEL. Эта аннотация указывает, что функция является вспомогательной, вызываемой из процедур отмены, и что она выполняет требования, которые предъявляются к функциям DRIVER_CANCEL (т. е. она выполняет обязательства для вызывающего).
Например, ниже приведено объявление типа функции обратного вызова DRIVER_CANCEL. Одним из параметров является IRQL, который должен быть восстановлен этой функцией. Заметки указывают все требования функции отмены.
// Define driver cancel routine type. //
__drv_functionClass(DRIVER_CANCEL)
_Requires_lock_held_(_Global_cancel_spin_lock_)
_Releases_lock_(_Global_cancel_spin_lock_)
_IRQL_requires_min_(DISPATCH_LEVEL)
_IRQL_requires_(DISPATCH_LEVEL)
typedef
VOID
DRIVER_CANCEL (
_Inout_ struct _DEVICE_OBJECT *DeviceObject,
_Inout_ _IRQL_uses_cancel_ struct _IRP *Irp
);
typedef DRIVER_CANCEL *PDRIVER_CANCEL;