Blocages de débogage - DRIVER_VERIFIER_DETECTED_VIOLATION (C4) : 0x1001
Lorsque le vérificateur de pilotes détecte une violation de hiérarchie de verrous de rotation, il génère la vérification de bogues 0xC4 : DRIVER_VERIFIER_DETECTED_VIOLATION avec une valeur de paramètre 1 de 0x1001.
Lorsque l’option de détection de blocage est active (la détection de blocage fait partie des options standard du vérificateur de pilotes), le vérificateur de pilotes effectue le suivi de chaque verrou de rotation alloué et de l’ordre dans lequel il a été acquis et libéré. Une violation de hiérarchie de verrouillage signifie que le vérificateur de pilotes a détecté une situation dans laquelle, dans au moins un cas, LockA est acquis et détenu avant LockB et dans un autre, LockB est acquis et détenu avant que LockA ne soit requis.
Important : cette case vérification de bogue se produit chaque fois que le vérificateur de pilotes détecte que la violation de hiérarchie s’est produite, et non lorsqu’une situation de blocage réelle survient. Cette violation oblige fortement à ce que tous les verrous partagés entre les différents composants d’un pilote soient toujours acquis et libérés dans un ordre qui rend impossible le blocage de deux threads.
Nouveautés de Windows 8.1 Lorsque le vérificateur de pilotes détecte cette violation, si le débogueur est attaché, il vous demande des informations sur l’erreur. Dans Windows 8 et les versions précédentes de Windows, cette violation entraîne une vérification de bogue immédiate.
************ Verifier Detected a Potential Deadlock *************
**
** Type !deadlock in the debugger for more information.
**
*****************************************************************
*** Verifier assertion failed ***
(B)reak, (I)gnore, (W)arn only, (R)emove assert?
Pour déboguer cette violation sur un ordinateur exécutant Windows 8.1, choisissez B (Arrêt), puis saisissez la commande de débogueur suggérée (!deadlock) :
kd> !deadlock
issue: 00001001 97dd800c 86858ce0 00000000
Deadlock detected (2 locks in 2 threads):
Thread 0: A B.
Thread 1: B A.
Where:
Thread 0 = TERMINATED.
Thread 1 = c4ae2040.
Lock A = 97dd800c (MyTestDriver!AlphaLock+0x00000000) - Type 'Spinlock'.
Lock B = 97dd8008 (MyTestDriver!BravoLock+0x00000000) - Type 'Spinlock'.
La commande !deadlock 3 peut également être utilisée pour afficher plus d’informations, y compris la pile au moment de la dernière acquisition :
kd> !deadlock 3
issue: 00001001 97dd800c 86858ce0 00000000
Deadlock detected (2 locks in 2 threads):
#
Thread 0: TERMINATED took locks in the following order:
Lock A = 97dd800c (MyTestDriver!AlphaLock+0x00000000) - Type 'Spinlock'.
Node: 8685acd8
Stack: 97dd65b7 MyTestDriver!SystemControlIrpWorker+0x00000027
97dd605a MyTestDriver!Dispatch_SystemControl+0x0000001a
820c4b4d nt!IovCallDriver+0x000002cc
81ca3772 nt!IofCallDriver+0x00000062
81eb165e nt!IopSynchronousServiceTail+0x0000016e
81eb5518 nt!IopXxxControlFile+0x000003e8
81eb4516 nt!NtDeviceIoControlFile+0x0000002a
81d27e17 nt!KiSystemServicePostCall+0x00000000
Lock B = 97dd8008 (MyTestDriver!BravoLock+0x00000000) - Type 'Spinlock'.
Node: 86833578
Stack: 97dd65c5 MyTestDriver!SystemControlIrpWorker+0x00000a4a
97dd605a MyTestDriver!Dispatch_SystemControl+0x0000001a
820c4b4d nt!IovCallDriver+0x000002cc
81ca3772 nt!IofCallDriver+0x00000062
81eb165e nt!IopSynchronousServiceTail+0x0000016e
81eb5518 nt!IopXxxControlFile+0x000003e8
81eb4516 nt!NtDeviceIoControlFile+0x0000002a
81d27e17 nt!KiSystemServicePostCall+0x00000000
#
Thread 1: c4ae2040 (ThreadEntry = 86833a08) took locks in the following order:
Lock B = 97dd8008 (MyTestDriver!BravoLock+0x00000000) - Type 'Spinlock'.
Node: 86858ce0
Stack: 97dd65ef MyTestDriver!DeviceControlIrpWorker+0x0000005f
97dd605a MyTestDriver!Dispatch_DeviceControl+0x0000001a
820c4b4d nt!IovCallDriver+0x000002cc
81ca3772 nt!IofCallDriver+0x00000062
81eb165e nt!IopSynchronousServiceTail+0x0000016e
81eb5518 nt!IopXxxControlFile+0x000003e8
81eb4516 nt!NtDeviceIoControlFile+0x0000002a
81d27e17 nt!KiSystemServicePostCall+0x00000000
Lock A = 97dd800c (MyTestDriver!AlphaLock+0x00000000) - Type 'Spinlock'.
Stack: << Current stack trace - use kb to display it >>
Le débogueur suggère d’utiliser la commande kb (Afficher l’arborescence des appels de procédure) pour afficher la trace de pile actuelle.
kd> kb
ChildEBP RetAddr Args to Child
89b2cac4 820da328 97dd800c 86858ce0 00000000 nt!VfReportIssueWithOptions+0x86
89b2caf4 820d92a2 00000001 00000000 97dd65fd nt!ViDeadlockAnalyze+0x1d1
89b2cb7c 820d424e 86858ce0 00000000 97dd65fd nt!VfDeadlockAcquireResource+0x2fd
89b2cb9c 97dd65fd 00007780 89b2cbbc 97dd605a nt!VerifierKfAcquireSpinLock+0x8c
89b2cba8 97dd605a 9a9e7780 88d08f48 00000000 MyTestDriver!DeviceControlIrpWorker+0x54a
89b2cbbc 820c4b4d 9a9e7780 88d08f48 820c4881 MyTestDriver!Dispatch_DeviceControl+0x1a
(Inline) -------- -------- -------- -------- nt!IopfCallDriver+0x47
89b2cbe0 81ca3772 81eb165e b3c9ff80 88d08f48 nt!IovCallDriver+0x2cc
89b2cbf4 81eb165e 88d08fdc 88d08f48 00000000 nt!IofCallDriver+0x62
La sortie du débogueur montre que le pilote en question a violé cette règle en acquérant et en détenant le verrou A avant d’acquérir le verrou B sur un thread, et qu’il a maintenant acquis le verrou B et tente d’acquérir le verrou A sur un autre thread. Il convient de noter que le premier thread (Thread 0) est arrêté, de sorte que l’acquisition et la libération ultérieure de ces deux verrous se sont produites à un moment donné depuis le chargement de l’image du pilote.
Lorsque les symboles appropriés pour le pilote de test sont chargés, le débogueur affiche la fonction dans laquelle le verrou a été acquis à ce moment-là. Dans cet exemple, il se trouve que le verrou A et le verrou B sont acquis dans la même fonction. Dans le Thread 0, ils sont tous deux acquis dans SystemControlIrpWorker. Dans le Thread 1, ils sont tous deux acquis dans DeviceControlIrpWorker (illustré dans la sortie du verrou B à partir de !deadlock 3 et la sortie de la pile actuelle (kb).
À ce stade, un examen du code source de chaque fonction devrait révéler l’existence d’un chemin de code permettant d’acquérir les verrous dans cet ordre.
Tant MyTestDriver!AlphaLock que MyTestDriver!BravoLock sont des objets globalement disponibles dans le pilote :
include "MyTestDriverHeader.h"
// Locks used to control access to various objects
extern KSPIN_LOCK AlphaLock;
extern KSPIN_LOCK BravoLock;
La fonction SystemControlIrpWorker contient un chemin où AlphaLock (verrou A dans la sortie !deadlock) est acquis et conservé lorsque BravoLock (verrou B) est acquis. Il est également important de noter que les verrous sont correctement libérés dans l’ordre inverse dans lequel ils sont acquis. (Le code suivant est largement modifié pour afficher uniquement les éléments requis pour générer ce scénario).
NTSTATUS SystemControlIrpWorker(_In_ PIRP Irp)
{
KIRQL IrqlAlpha;
KIRQL IrqlBravo;
// <<Other local variable declarations removed>>
// <<Various lines of code not shown>>
KeAcquireSpinLock (&AlphaLock, &IrqlAlpha);
// <<Various lines of code not shown>>
KeAcquireSpinLock (&BravoLock, &IrqlBravo);
// <<Various lines of code not shown>>
KeReleaseSpinLock (&BravoLock, IrqlBravo);
KeReleaseSpinLock (&AlphaLock, IrqlAlpha);
// <<Various lines of code not shown>>
}
Si vous passez en revue l’exemple de fonction DeviceControlIrpWorker suivant, vous pouvez voir qu’il est possible d’acquérir les verrous dans l’ordre inverse. Autrement dit, BravoLock peut être acquis et détenu lors de la tentative d’acquisition d’AlphaLock. L’exemple suivant est simplifié, mais il montre qu’il existe un chemin d’accès possible où une violation peut se produire.
NTSTATUS DeviceControlIrpWorker(_In_ PIRP Irp,
_In_ BOOLEAN bSomeCondition)
{
KIRQL IrqlAlpha;
KIRQL IrqlBravo;
// <<Other local variable declarations removed>>
if (bSomeCondition == FALSE)
{
//
// Note that if bSomeCondition is FALSE, then AlphaLock is acquired here
// If bSomeCondition is TRUE, it is not needed to be acquired right now
//
KeAcquireSpinLock (&AlphaLock, &IrqlAlpha);
}
// <<Various lines of code not shown>>
KeAcquireSpinLock (&BravoLock, &IrqlBravo);
// <<Various lines of code not shown>>
if (bSomeCondition == TRUE)
{
//
// Need to acquire AlphaLock here for upcoming code logic
//
KeAcquireSpinLock (&AlphaLock, &IrqlAlpha);
// <<Various lines of code not shown>>
KeReleaseSpinLock (&AlphaLock, IrqlAlpha);
}
// <<Various lines of code not shown>>
KeReleaseSpinLock (&BravoLock, IrqlBravo);
if (bSomeCondition == FALSE)
{
//
// Release the AlphaLock, which was acquired much earlier
//
KeReleaseSpinLock (&AlphaLock, IrqlAlpha);
}
// <<Various lines of code not shown>>
}
Pour remédier à cette violation potentielle, il convient de s’assurer que chaque fois que le pilote tente d’acquérir AlphaLock, il vérifie et s’assure que BravoLock n’est pas détenu. La solution la plus simple consiste à libérer BravoLock et à le réacquérir immédiatement après l’acquisition d’AlphaLock. Mais des modifications du code plus importantes peuvent s’avérer nécessaires s’il est vital que les données que BravoLock protège ne changent pas pendant l’attente d’AlphaLock et de la ré-acquisition de BravoLock.
Pour en savoir plus sur les verrous de rotation et d’autres techniques de synchronisation, consultez Verrous de rotation.
Rubriques connexes
Vérification des bogues 0xCA : DRIVER_VERIFIER_DETECTED_VIOLATION