Injection d’échec basée sur la pile

Note Les instructions pour activer cette fonctionnalité s’appliquent uniquement au WDK pour Windows 8. Par Windows 8.1, cette fonctionnalité a été intégrée à Driver Verifier. Sur les ordinateurs exécutant Windows 8.1, utilisez l’option De simulation systématique de faibles ressources.

L’option Stack Based Failure Injection injecte les échecs de ressources dans les pilotes en mode noyau. Cette option utilise un pilote spécial, KmAutoFail.sys, conjointement avec Driver Verifier pour pénétrer les chemins de gestion des erreurs du pilote. L’essai de ces chemins a toujours été très difficile. L’option Stack Based Failure Injection injecte les défaillances de ressources de manière prévisible, ce qui rend les problèmes qu’elle détecte reproductibles. Étant donné que les chemins d’erreur sont faciles à reproduire, il est également facile de vérifier les correctifs à ces problèmes.

Pour vous aider à déterminer la cause racine de l’erreur, une extension de débogueur est fournie qui peut vous indiquer exactement quelles défaillances ont été injectées et dans quel ordre.

Lorsque l’option Injection d’échec basée sur la pile est activée sur un pilote spécifique, elle intercepte certains appels de ce pilote vers le noyau et Ndis.sys. L’injection d’échec basée sur la pile examine la pile d’appels, en particulier la partie de la pile d’appels qui provient du pilote sur lequel elle est activée. Si c’est la première fois qu’il voit cette pile, l’appel échoue en fonction de la sémantique de cet appel. Sinon, s’il a déjà vu cet appel, il le passera sans avoir été touché. Stack Based Failure Injection contient une logique pour gérer le fait qu’un pilote peut être chargé et déchargé plusieurs fois. Il reconnaît qu’une pile d’appels est la même même si le pilote est rechargé dans un autre emplacement de mémoire.

Activation de cette option

Vous pouvez activer la fonctionnalité d’injection d’échec basée sur la pile pour un ou plusieurs pilotes lorsque vous déployez un pilote sur un ordinateur de test. Vous pouvez sélectionner l’option Injection d’échec basée sur la pile lorsque vous configurez les propriétés du vérificateur de pilotes pour les projets de package de pilotes. Vous devez redémarrer l’ordinateur pour activer ou désactiver l’option Injection d’échec basée sur la pile. Vous pouvez également exécuter un utilitaire de test pour activer Driver Verifier et cette fonctionnalité sur l’ordinateur de test.

Important Lorsque vous activez l’injection d’échec basée sur la pile sur l’ordinateur de test, veillez à ne pas également sélectionner Simulation de ressources faibles.

  • Page Utilisation de la propriété driver verifier

    1. Ouvrez les pages de propriétés de votre package de pilotes. Cliquez avec le bouton droit sur le projet de package de pilote dans Explorateur de solutions, puis sélectionnez Propriétés.
    2. Dans les pages de propriétés du package de pilotes, cliquez sur Propriétés de configuration, sur Installer le pilote, puis sur Vérificateur de pilote.
    3. Sélectionnez Activer le vérificateur de pilotes. Lorsque vous activez Driver Verifier sur l’ordinateur de test, vous pouvez choisir d’activer Driver Verifier pour tous les pilotes de l’ordinateur, pour le projet de pilote uniquement, ou pour une liste de pilotes spécifiés.
    4. Sous Injecteur d’échec basé sur la pile, sélectionnez (case activée) Injection d’échec basée sur la pile.
    5. Cliquez sur Appliquer ou OK.
    6. Pour plus d’informations, consultez Déploiement d’un pilote sur un ordinateur de test . L’ordinateur de test doit redémarrer pour activer cette option.
  • Utilisation du test Activer et désactiver le vérificateur de pilote

    1. Vous pouvez également activer Driver Verifier en exécutant un test de l’utilitaire. Suivez les instructions décrites dans Comment tester un pilote au moment de l’exécution à l’aide de Visual Studio. Sous la catégorie de test Tous les tests\Vérificateur de pilotes, sélectionnez les tests Activer le vérificateur de pilote (redémarrage possible requis) et Désactiver le vérificateur de pilote (redémarrage possible requis).

    2. Sélectionnez les options du vérificateur de pilotes en cliquant sur le nom du test Activer le vérificateur de pilote (redémarrage possible requis) dans la fenêtre Groupe de tests de pilotes .

    3. Sélectionnez (case activée) Injection d’échec basée sur la pile.

    4. Après avoir ajouté ces tests à un groupe de tests, vous pouvez enregistrer le groupe de tests. Pour activer l’injection d’échec basée sur la pile, exécutez le test Activer le vérificateur de pilote (redémarrage possible requis) sur l’ordinateur que vous avez configuré pour le test.

      Pour désactiver le vérificateur de pilotes, exécutez le test Désactiver le vérificateur de pilote (redémarrage possible requis).

Utilisation de l’option d’injection d’échec basée sur la pile

Une considération importante lors du test avec l’injection d’échec basée sur la pile est que la plupart des bogues trouvés entraînent un bogue case activée. Cela peut s’avérer quelque peu douloureux si votre pilote est un pilote chargé au démarrage. Pour cette raison, nous désactivons automatiquement l’injection d’échec basée sur la pile si le vérificateur de pilote est désactivé. Cela signifie que vous pouvez désactiver l’injection d’échec basée sur la pile au démarrage à partir du débogueur en désactivant Driver Verifier à l’aide de la commande !verifier –disable.

Si cela est possible, pour vos tests initiaux avec l’injection d’échec basée sur la pile, configurez votre pilote afin qu’il ne soit pas chargé au démarrage. Vous pouvez ensuite exécuter des tests de chargement et de déchargement simples. La plupart des bogues trouvés par l’injection d’échecs basés sur la pile se produisent pendant l’initialisation ou le nettoyage. Le chargement et le déchargement répétés de votre chauffeur sont un bon moyen de les trouver.

Après avoir apporté les correctifs nécessaires pour que les tests de déchargement de charge réussissent, vous pouvez passer aux tests basés sur IOCTL, aux tests fonctionnels complets et enfin aux tests de contrainte. En général, si vous suivez cette progression de test, vous ne découvrirez pas beaucoup de nouveaux problèmes pendant les tests de contrainte, car la plupart des chemins de code auront déjà été exécutés avant cela.

Utilisation de l’extension de débogueur SBFI (Stack Based Failure Injection)

La plupart des problèmes détectés avec l’injection d’échec de pile entraînent des vérifications de bogues. Pour aider à déterminer la cause de ces bogues de code, le WDK fournit l’extension du débogueur Stack Based Failure Injection et les symboles nécessaires. La procédure d’installation installe les deux sur votre système de débogueur. L’emplacement par défaut est C:\Program Files (x86)\Windows Kits\8.0\Debuggers\<arch>.

Pour exécuter l’extension débogueur

  • À partir de l’invite de commandes du débogueur, tapez la commande suivante : !<path>\kmautofaildbg.dll.autofail. Par exemple, en supposant que les extensions de débogueur sont installées sur c:\dbgext et que kmautofail.pdb se trouve dans le chemin du symbole, vous devez entrer la commande suivante :

    !c:\dbgext\kmautofaildbg.dll.autofail
    

Cela permet de vider les informations sur votre débogueur montrant les piles d’appels des échecs les plus récents injectés. Chaque entrée ressemble à ce qui suit, tiré d’une série de tests réels. Dans l’exemple suivant, l’injection d’échec basée sur la pile est activée sur Mydriver.sys

Sequence: 2, Test Number: 0, Process ID: 0, Thread ID: 0
                 IRQ Level: 2, HASH: 0xea98a56083aae93c
 0xfffff8800129ed83 kmautofail!ShimHookExAllocatePoolWithTag+0x37
 0xfffff88003c77566 mydriver!AddDestination+0x66
 0xfffff88003c5eeb2 mydriver!ProcessPacketDestination+0x82
 0xfffff88003c7db82 mydriver!ProcessPacketSource+0x8b2
 0xfffff88003c5d0d8 mydriver!ForwardPackets+0xb8
 0xfffff88003c81102 mydriver!RoutePackets+0x142
 0xfffff88003c83826 mydriver!RouteNetBufferLists+0x306
 0xfffff88003c59a76 mydriver!DeviceSendPackets+0x156
 0xfffff88003c59754 mydriver!ProcessingComplete+0x4a4
 0xfffff88001b69b81 systemdriver2!ProcessEvent+0x1a1
 0xfffff88001b3edc4 systemdriver1!CallChildDriver+0x20
 0xfffff88001b3fc0a systemdriver1!ProcessEvent+0x3e
 0xfffff800c3ea6eb9 nt!KiRetireDpcList+0x209
 0xfffff800c3ea869a nt!KiIdleLoop+0x5a

En haut de la sortie, le numéro de séquence compte le nombre d’erreurs injectées. Cet exemple montre la deuxième erreur injectée pendant cette série de tests. L’ID de processus étant 0, il s’agissait du processus système. IRQL est 2, ce qui est appelé au niveau de la répartition.

À partir de la pile, KmAutoFail est le pilote d’injection d’échec basée sur la pile. Le nom de la fonction KmAutoFail indique l’appel de fonction de Mydriver.sys a été intercepté et injecté. Ici, la fonction qui a échoué était ExAllocatePoolWithTag. Toutes les fonctions de KmAutoFail qui interceptent les appels à Ntoskrnl.sys ou Ndis.sys utilisent cette convention de nommage. Ensuite, nous voyons la pile des appels avec le pilote testé (Mydriver.sys). Il s’agit de la partie de la pile des appels utilisée pour déterminer l’unicité de la pile. Par conséquent, chaque entrée vidée par l’extension du débogueur sera unique dans cette partie de la pile des appels. Le reste de la pile d’appels indique qui a appelé le pilote. La main’importance de cette opération est de savoir si le pilote est appelé en mode utilisateur (au moyen d’un IOCTL) ou à partir d’un pilote en mode noyau.

Notez que si un pilote a retourné un échec à partir de sa routine DriverEntry , une tentative de rechargement se produit normalement à un autre emplacement de mémoire. Dans ce cas, la pile d’appels de l’emplacement précédent contiendra probablement des informations « garbage » plutôt que des informations de pile provenant du pilote. Mais ce n’est pas un problème; il vous indique que le pilote a correctement géré cette erreur injectée.

Cette entrée suivante montre un appel au pilote au moyen d’un IOCTL à partir du mode utilisateur. Notez l’ID de processus et le niveau IRQ. Étant donné que Mydriver.sys est un pilote de filtre NDIS, le IOCTL est passé par Ndis.sys. Notez que nt! NtDeviceIoControlFile se trouve sur la pile. Tout test que vous exécutez sur votre pilote qui utilise iocTL passera par cette fonction.

Sequence: 5, Test Number: 0, Process ID: 2052, Thread ID: 4588
                 IRQ Level: 0, HASH: 0xecd4650e9c25ee4
 0xfffff8800129ed83 kmautofail!ShimHookExAllocatePoolWithTag+0x37
 0xfffff88003c6fb39 mydriver!SendMultipleOids+0x41
 0xfffff88003c7157b mydriver!PvtDisconnect+0x437
 0xfffff88003c71069 mydriver!NicDisconnect+0xd9
 0xfffff88003ca3538 mydriver!NicControl+0x10c
 0xfffff88003c99625 mydriver!DeviceControl+0x4c5
 0xfffff88001559d93 NDIS!ndisDummyIrpHandler+0x73
 0xfffff88001559339 NDIS!ndisDeviceControlIrpHandler+0xc9
 0xfffff800c445cc96 nt!IovCallDriver+0x3e6
 0xfffff800c42735ae nt!IopXxxControlFile+0x7cc
 0xfffff800c4274836 nt!NtDeviceIoControlFile+0x56
 0xfffff800c3e74753 nt!KiSystemServiceCopyEnd+0x13

Analyse des résultats de l’injection d’échec basée sur la pile

Vous exécutez vos tests sur votre pilote, et tout à coup, vous rencontrez un problème. Il s’agit probablement d’un bogue case activée, mais cela peut également être dû au fait que l’ordinateur ne répond plus. Comment trouver la cause ? En supposant qu’il s’agit d’un bogue case activée, utilisez d’abord l’extension ci-dessus pour rechercher la liste des échecs injectés, puis utilisez la commande du débogueur : !analyze –v.

Le case activée de bogue le plus courant est dû à la non-vérification de la réussite d’une allocation. Dans ce cas, la pile de l’analyse du bogue case activée est probablement presque identique à celle de la dernière défaillance injectée. À un moment donné après l’échec de l’allocation (souvent la ligne suivante), le pilote accède au pointeur null. Ce type de bogue est très facile à corriger. Parfois, l’allocation défaillante est un ou deux dans la liste, mais ce type est toujours très facile à trouver et à corriger.

Le deuxième case activée de bogue le plus courant se produit pendant le nettoyage. Dans ce cas, le pilote a probablement détecté l’échec d’allocation et a sauté vers le nettoyage ; mais pendant le nettoyage, le pilote n’a pas case activée le pointeur et a de nouveau accédé à un pointeur null. Un cas étroitement lié est où le nettoyage peut être appelé deux fois. Si le nettoyage ne définit pas le pointeur vers une structure sur null après l’avoir libérée, la deuxième fois que la fonction de nettoyage est appelée, elle essaie de libérer la structure une deuxième fois, ce qui entraîne un bogue case activée.

Les erreurs qui empêchent l’ordinateur de répondre sont plus difficiles à diagnostiquer, mais la procédure de débogage est similaire. Ces erreurs sont souvent dues à des problèmes de nombre de références ou de verrouillage de rotation. Heureusement, driver verifier intercepte de nombreux problèmes de verrouillage de rotation avant qu’ils n’entraînent des problèmes. Dans ce cas, effectuez une pause dans le débogueur et utilisez l’extension du débogueur pour vider la liste des erreurs qui ont été injectées par l’injection d’échecs basée sur la pile. Un aperçu rapide du code autour des dernières défaillances peut afficher un nombre de références qui est effectué avant l’échec, mais qui n’est pas publié après. Si ce n’est pas le cas, recherchez un thread dans votre pilote qui attend sur un verrou tournant, ou pour tout nombre de références qui est manifestement incorrect.