Suivi de référence d’objet avec des balises

Les objets de noyau sont des objets de données primitifs que le noyau Windows implémente dans la mémoire système. Ils représentent des entités telles que des appareils, des pilotes, des fichiers, des clés de Registre, des événements, des sémaphores, des processus et des threads.

La plupart des objets de noyau ne sont pas permanents. Pour empêcher Windows de supprimer un objet noyau non actif lorsqu’un pilote en mode noyau l’utilise, le pilote obtient une référence comptabilisée à l’objet. Lorsque le pilote n’a plus besoin de l’objet, le pilote libère sa référence à l’objet.

Si un pilote ne libère pas toutes ses références à un objet, le nombre de références de l’objet n’atteint jamais zéro et le Gestionnaire d’objets ne le supprime jamais. Vous ne pouvez pas réutiliser les ressources divulguées tant que le système d’exploitation n’a pas redémarré.

Un autre type d’erreur de référence se produit si un pilote sous fait référence à un objet. Dans ce cas, le pilote libère plus de références à un objet que le pilote ne détient réellement. Cette erreur peut entraîner la suppression prématurée de l’objet par le Gestionnaire d’objets, alors que d’autres clients essaient toujours d’accéder à l’objet.

Les fuites et les sous-références d’objets du noyau peuvent être des bogues difficiles à détecter. Par exemple, un objet de processus ou un objet d’appareil peut avoir des dizaines de milliers de références. Dans ces circonstances, il peut être difficile d’identifier la source d’un bogue de référence d’objet.

Dans Windows 7 et les versions ultérieures de Windows, vous pouvez fournir une balise pour les références d’objets afin de faciliter la recherche de ces bogues. Les routines suivantes associent des balises à l’acquisition et à la publication de références aux objets du noyau :

ObDereferenceObjectDeferDeleteWithTag

ObDereferenceObjectWithTag

ObReferenceObjectByHandleWithTag

ObReferenceObjectByPointerWithTag

ObReferenceObjectWithTag

Par exemple, ObReferenceObjectWithTag et ObDereferenceObjectWithTag, qui sont disponibles dans Windows 7 et versions ultérieures de Windows, sont des versions améliorées des routines ObReferenceObject et ObDereferenceObject , qui sont disponibles dans Windows 2000 et versions ultérieures de Windows. Ces routines améliorées vous permettent de fournir une valeur d’étiquette personnalisée de quatre octets en tant que paramètre d’entrée. Vous pouvez utiliser les outils de débogage Windows pour inspecter une trace de référence d’objet qui contient la valeur de balise pour chaque appel . ObReferenceObject et ObDereferenceObject ne permettent pas à l’appelant de spécifier des balises personnalisées, mais, dans Windows 7 et les versions ultérieures de Windows, ces routines ajoutent des balises par défaut (avec la valeur de balise « Dflt ») à la trace. Par conséquent, un appel à ObReferenceObject ou ObDereferenceObject a le même effet qu’un appel à ObReferenceObjectWithTag ou ObDereferenceObjectWithTag qui spécifie une valeur de balise « Dflt ». (Dans votre programme, cette valeur de balise s’affiche sous la forme 0x746c6644 ou « tlfD ».

Pour rechercher une fuite d’objet potentielle ou une sous-référence, identifiez un ensemble d’appels ObReferenceObjectXxxWithTag et ObDereferenceObjectXxxWithTag associés dans votre pilote qui incrémentent et décrémentent le nombre de références d’un objet particulier. Choisissez une valeur de balise commune (par exemple, « Lky8 ») à utiliser pour tous les appels de cet ensemble. Une fois que votre pilote a terminé d’utiliser un objet, le nombre de décréments doit correspondre exactement au nombre d’incréments. Si ces nombres ne correspondent pas, votre pilote présente un bogue de référence d’objet. Le débogueur peut comparer le nombre d’incréments et de décréments pour chaque valeur de balise et vous indiquer s’ils ne correspondent pas. Grâce à cette fonctionnalité, vous pouvez rapidement identifier la source de l’incompatibilité entre le nombre de références et le nombre de références.

Pour afficher une trace de référence d’objet dans les outils de débogage Windows, utilisez l’extension !obtrace du débogueur en mode noyau. Si le suivi de référence d’objet est activé, vous pouvez utiliser l’extension !obtrace pour afficher les balises de référence d’objet. Par défaut, le suivi de référence d’objet est désactivé. Utilisez l’éditeur d’indicateurs globaux (Gflags) pour activer le suivi de référence d’objet. Pour plus d’informations sur Gflags, consultez Configuration du suivi de référence d’objet.

La sortie de l’extension !obtrace inclut une colonne « Tag », comme le montre l’exemple suivant :

0: kd> !obtrace 0x8a226130
Object: 8a226130
 Image: leakyapp.exe
Sequence   (+/-)   Tag    Stack
--------   -----   ----   --------------------------------------------
      36    +1     Dflt      nt!ObCreateObject+1c4
                             nt!NtCreateEvent+93
                             nt!KiFastCallEntry+12a

      37    +1     Dflt      nt!ObpCreateHandle+1c1
                             nt!ObInsertObjectEx+d8
                             nt!ObInsertObject+1e
                             nt!NtCreateEvent+ba
                             nt!KiFastCallEntry+12a

      38    -1     Dflt      nt!ObfDereferenceObjectWithTag+22
                             nt!ObInsertObject+1e
                             nt!NtCreateEvent+ba
                             nt!KiFastCallEntry+12a

      39    +1     Lky8      nt!ObReferenceObjectByHandleWithTag+254
                             leakydrv!LeakyCtlDeviceControl+6c
                             nt!IofCallDriver+63
                             nt!IopSynchronousServiceTail+1f8
                             nt!IopXxxControlFile+6aa
                             nt!NtDeviceIoControlFile+2a
                             nt!KiFastCallEntry+12a

      3a    -1     Dflt      nt!ObfDereferenceObjectWithTag+22
                             nt!ObpCloseHandle+7f
                             nt!NtClose+4e
                             nt!KiFastCallEntry+12a
 
--------   -----   ----   --------------------------------------------
References: 3, Dereferences 2
Tag: Lky8 References: 1 Dereferences: 0 Over reference by: 1

La dernière ligne de cet exemple indique que les nombres de références et de déréférencements associés à la balise « Lky8 » ne correspondent pas et que le résultat de cette incompatibilité est une référence excessive par un (autrement dit, une fuite).

Si le résultat était plutôt une sous-référence, la dernière ligne de la sortie !obtrace peut être la suivante :

Tag: Lky8 References: 1 Dereferences: 2 Under reference by: 1

Par défaut, le système d’exploitation conserve la mémoire en supprimant la trace de référence d’objet pour un objet après avoir libéré l’objet. Il peut être utile de conserver une trace en mémoire même après que le système libère un objet lors de la poursuite d’une sous-référence. À cet effet, l’outil Gflags fournit une option « Permanent », qui conserve la trace en mémoire pendant que l’ordinateur s’arrête et redémarre.

Windows XP a introduit le suivi de référence d’objet. Étant donné qu’initialement la trace n’incluait pas d’étiquettes, les développeurs devaient utiliser des techniques moins pratiques pour identifier les bogues de référence d’objets. Le débogueur peut suivre les références de groupes d’objets, que le développeur a sélectionnés par type d’objet. La seule façon pour le développeur d’identifier les différentes sources de références et de déréférences d’objets était de comparer leurs piles d’appels. Bien que l’exemple !obtrace précédent ne contienne que cinq piles, certains types d’objet, tels qu’un objet de processus (EPROCESS), peuvent être référencés et déréférencés plusieurs milliers de fois. Avec des milliers de piles à inspecter, il peut être difficile d’identifier la source d’une fuite d’objet ou d’une sous-référence sans utiliser de balises.