Remarque
L’accès à cette page requiert une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page requiert une autorisation. Vous pouvez essayer de modifier des répertoires.
Tous les développeurs de pilotes doivent prendre en compte les niveaux de demande d’interruption (IRQL). Un IRQL est un entier compris entre 0 et 31 ; PASSIVE_LEVEL, DISPATCH_LEVEL et APC_LEVEL sont normalement référencés symboliquement, et les autres par leurs valeurs numériques. Le relèvement et l'abaissement de l'IRQL doivent respecter une stricte discipline de pile. Une fonction doit viser à retourner au même IRQL auquel elle a été appelée. Les valeurs IRQL doivent être non décroissantes dans la pile. Et une fonction ne peut pas abaisser l'IRQL sans d'abord l'élever. Les annotations IRQL sont destinées à aider à appliquer ces règles.
Lorsque le code du pilote a des annotations IRQL, les outils d’analyse du code peuvent améliorer l’inférence sur la plage de niveaux à laquelle une fonction doit s’exécuter et trouver plus précisément des erreurs. Par exemple, vous pouvez ajouter des annotations qui spécifient la valeur IRQL maximale à laquelle une fonction peut être appelée ; si une fonction est appelée à un irQL supérieur, les outils d’analyse du code peuvent identifier les incohérences.
Les fonctions de pilote doivent être annotées avec autant d’informations sur l’IRQL qui peut être appropriée. Si des informations supplémentaires sont disponibles, les outils d’analyse du code permettent de vérifier à la fois la fonction appelante et la fonction appelée. Dans certains cas, l’ajout d’une annotation est un bon moyen de supprimer un faux positif. Certaines fonctions, telles qu’une fonction utilitaire, peuvent être appelées à n’importe quel irQL. Dans ce cas, l’absence d’annotation IRQL est l’annotation appropriée.
Lors de l’annotation d’une fonction pour IRQL, il est particulièrement important de prendre en compte la façon dont la fonction peut évoluer, pas seulement son implémentation actuelle. Par exemple, une fonction implémentée peut fonctionner correctement à un irQL supérieur à celui prévu par le concepteur. Bien qu’il soit tentant d’annoter la fonction en fonction de ce que fait réellement le code, le concepteur peut être conscient des exigences futures, telles que la nécessité de réduire la valeur IRQL maximale pour une amélioration future ou une configuration système en attente. L’annotation doit être dérivée de l’intention du concepteur de fonctions, et non de l’implémentation réelle.
Vous pouvez utiliser les annotations dans le tableau suivant pour indiquer l'IRQL correct pour une fonction et ses paramètres. Les valeurs IRQL sont définies dans Wdm.h.
| Annotation IRQL | Descriptif |
|---|---|
| _IRQL_requires_max_(irql) | L’irql est le paramètre IRQL maximal auquel la fonction peut être appelée. |
| _IRQL_requires_min_(irql) | L'IRQL est le niveau IRQL minimal auquel la fonction peut être appelée. |
| _IRQL_requires_(irql) | La fonction doit être atteinte au niveau IRQL spécifié par irql. |
| _IRQL_raises_(irql) | La fonction se termine au niveau d'IRQL spécifié, mais elle ne peut être appelée que pour augmenter (et non abaisser) l'IRQL actuel. |
| _IRQL_saves_ | Le paramètre annoté enregistre le runtime d’intégration actuel pour restaurer ultérieurement. |
| _IRQL_restores_ | Le paramètre annoté contient une valeur IRQL de IRQL_saves qui doit être restaurée lorsque la fonction retourne. |
| _IRQL_saves_global_(kind, param) | Le runtime d’intégration actuel est enregistré dans un emplacement interne aux outils d’analyse du code à partir desquels le runtime d’intégration doit être restauré. Cette annotation est utilisée pour annoter une fonction. L’emplacement est identifié par catégorie et affiné par paramètre. Par exemple, OldIrql peut être le type, et FastMutex peut être le paramètre qui contenait cette ancienne valeur IRQL. |
| _IRQL_restores_global_(kind, param) | Le niveau IRQL enregistré par la fonction annotée avec IRQL_saves_global est restauré depuis un emplacement interne aux outils d'analyse de code. |
| _IRQL_always_function_min_(value) | La valeur IRQL est la valeur minimale à laquelle la fonction peut abaisser l'IRQL. |
| _IRQL_always_function_max_(value) | La valeur IRQL est la valeur maximale à laquelle la fonction peut déclencher l’IRQL. |
| _IRQL_requires_same_ | La fonction annotée doit entrer et sortir avec le même IRQL. La fonction peut modifier l’IRQL, mais elle doit restaurer l’IRQL à sa valeur d’origine avant de quitter. |
| _IRQL_uses_cancel_ | Le paramètre annoté est la valeur IRQL qui doit être restaurée par une fonction de rappel DRIVER_CANCEL. Dans la plupart des cas, utilisez plutôt l’annotation IRQL_is_cancel. |
Annotations pour DRIVER_CANCEL
Il existe une différence entre les annotations _IRQL_uses_cancel_ et _IRQL_is_cancel_. L’annotation _IRQL_uses_cancel_ spécifie simplement que le paramètre annoté est la valeur IRQL qui doit être restaurée par une fonction de rappel DRIVER_CANCEL. L’annotation _IRQL_is_cancel_ est une annotation composite qui se compose de _IRQL_uses_cancel_ plus plusieurs autres annotations qui garantissent un comportement correct d’une fonction utilitaire de rappel DRIVER_CANCEL. Par lui-même, l’annotation _IRQL_uses_cancel_ n’est que occasionnellement utile ; par exemple, si le reste des obligations décrites par _IRQL_is_cancel_ ont déjà été remplies d’une autre manière.
| Annotation IRQL | Descriptif |
|---|---|
| _IRQL_is_cancel_ | Le paramètre annoté est l’IRQL passé dans le cadre de l’appel à une fonction de rappel DRIVER_CANCEL. Cette annotation indique que la fonction est un utilitaire appelé à partir des routines Cancel et qui répond aux exigences des fonctions DRIVER_CANCEL, y compris la libération du verrou de rotation d’annulation. |
Interaction des annotations IRQL
Les annotations de paramètre IRQL interagissent entre elles plus que d’autres annotations, car la valeur IRQL est définie, réinitialisée, enregistrée et restaurée par les différentes fonctions appelées.
Spécification du nombre maximal et minimal d’IRQL
Les annotations _IRQL_requires_max_ et _IRQL_requires_min_ spécifient que la fonction ne doit pas être appelée à partir d’un IRQL supérieur ou inférieur à la valeur spécifiée. Par exemple, lorsque PREfast voit une séquence d’appels de fonction qui ne modifient pas le niveau de requête d'interruption (IRQL), s'il en trouve un avec une valeur _IRQL_requires_max_ inférieure à un _IRQL_requires_min_ à proximité, il signale un avertissement lors du deuxième appel qu’il rencontre. L’erreur peut se produire dans le premier appel ; le message indique où l’autre moitié du conflit s’est produite.
Si les annotations d’une fonction mentionnent l'IRQL et ne s’appliquent pas explicitement _IRQL_requires_max_, l’outil d’Analyse du code applique implicitement l’annotation _IRQL_requires_max_(DISPATCH_LEVEL), qui est généralement correcte, sauf dans de rares exceptions. L’application implicite de ceci en tant que valeur par défaut élimine beaucoup d’encombrement des annotations, ce qui rend les exceptions beaucoup plus visibles.
L’annotation _IRQL_requires_min_(PASSIVE_LEVEL) est toujours implicite, car l’IRQL ne peut pas être réduit ; par conséquent, il n’existe aucune règle explicite correspondante concernant le niveau minimum d'IRQL. Très peu de fonctions ont à la fois une limite supérieure autre que DISPATCH_LEVEL et une limite inférieure autre que PASSIVE_LEVEL.
Certaines fonctions sont appelées dans un contexte dans lequel la fonction appelée ne peut pas élever en toute sécurité l’IRQL au-dessus d’un maximum ou, plus souvent, ne peut pas la réduire en toute sécurité en dessous d’un minimum. Les annotations _IRQL_always_function_max_ et _IRQL_always_function_min_ aident PREfast à trouver des cas où cela se produit involontairement.
Par exemple, les fonctions de type DRIVER_STARTIO sont annotées avec _IRQL_always_function_min_(DISPATCH_LEVEL). Cela signifie que pendant l’exécution d’une fonction DRIVER_STARTIO, il s’agit d’une erreur de réduire le niveau IRQL au-dessous de DISPATCH_LEVEL. D’autres annotations indiquent que la fonction doit être entrée et quittée à DISPATCH_LEVEL.
Spécification d’un IRQL explicite
Utilisez l’annotation _IRQL_raises_ ou _IRQL_requires_ pour aider PREfast à mieux signaler une incohérence qui est découverte avec des annotations _IRQL_requires_max_ ou _IRQL_requires_min_, car elle connaît ensuite l’IRQL.
L’annotation _IRQL_raises_ indique qu’une fonction retourne avec la valeur IRQL définie sur une nouvelle valeur. Lorsque vous utilisez l’annotation _IRQL_raises_, elle définit également efficacement l’annotation _drv_maxFunctionIRQL sur la même valeur IRQL. Toutefois, si la fonction élève la valeur IRQL au-dessus de la valeur finale, puis la réduit à cette valeur finale, vous devez ajouter une annotation explicite _IRQL_always_function_max_ après l’annotation _IRQL_raises_ pour permettre la valeur IRQL supérieure.
Élévation ou réduction du runtime d’intégration
L’annotation _IRQL_raises_ indique que la fonction doit être utilisée uniquement pour déclencher IRQL et ne doit pas être utilisée pour réduire irQL, même si la syntaxe de la fonction l’autoriserait. KeRaiseIrql est un exemple de fonction qui ne doit pas être utilisée pour réduire IRQL.
Enregistrement et restauration d’IRQL
Utilisez les annotations _IRQL_saves_ et _IRQL_restores_ pour indiquer que l’IRQL actuel (qu’il soit connu exactement ou seulement environ) est enregistré ou restauré à partir du paramètre annoté.
Certaines fonctions enregistrent et restaurent implicitement l’IRQL. Par exemple, la fonction système ExAcquireFastMutex enregistre l’IRQL dans un emplacement opaque associé à l’objet mutex rapide identifié par le premier paramètre ; l’IRQL sauvegardé est restauré par la fonction ExReleaseFastMutex correspondante pour cet objet mutex rapide. Pour indiquer explicitement ces actions, utilisez les annotations _IRQL_saves_global_ et _IRQL_restores_global_. Les paramètres type et paramètre indiquent où la valeur IRQL est enregistrée. L’emplacement où la valeur est enregistrée n’a pas besoin d’être spécifiée précisément, tant que les annotations qui enregistrent et restaurent la valeur sont cohérentes.
Gestion du même IRQL
Vous devez annoter toutes les fonctions que votre pilote crée qui modifient le runtime d’intégration à l’aide de l’annotation _IRQL_requires_same_ ou de l’une des autres annotations IRQL pour indiquer que la modification dans IRQL est attendue. En l’absence d’annotations qui indiquent toute modification dans IRQL, les outils d’analyse du code émettent un avertissement pour toute fonction qui ne se ferme pas au même IRQL auquel la fonction a été entrée. Si la modification dans IRQL est prévue, ajoutez l’annotation appropriée pour supprimer l’erreur. Si la modification dans IRQL n’est pas prévue, le code doit être corrigé.
Enregistrement et restauration d’IRQL pour les routines d’annulation d’E/S
Utilisez l’annotation _IRQL_uses_cancel_ pour indiquer que le paramètre annoté est la valeur IRQL qui doit être restaurée par une fonction de rappel DRIVER_CANCEL. Cette annotation indique que la fonction est une fonction utilitaire appelée à partir de routines d’annulation et qu’elle achève les exigences qui ont été imposées sur les fonctions DRIVER_CANCEL (autrement dit, elle libère l'appelant de l'obligation).
Par exemple, voici la déclaration du type de fonction de rappel DRIVER_CANCEL. L’un des paramètres est l’IRQL qui doit être restauré par cette fonction. Les annotations indiquent toutes les exigences d’une fonction d’annulation.
// 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;