Freigeben über


Debuggen von Deadlocks – DRIVER_VERIFIER_DETECTED_VIOLATION (C4): 0x1001

Wenn die Treiberüberprüfung eine Spinlock-Hierarchieverletzung erkennt, generiert die Spinlock-Hierarchieverletzung Bug Check 0xC4: DRIVER_VERIFIER_DETECTED_VIOLATION mit einem Parameter 1-Wert von 0x1001.

Wenn die Deadlock-Erkennungsoption aktiv ist (die Deadlock-Erkennung ist Bestandteil der Standardoptionen von Treiberüberprüfung), verfolgt die Treiberüberprüfung die einzelnen zugewiesenen Spinlock und die Reihenfolge, in der sie erworben und freigegeben wurde. Eine Verletzung der Lockhierarchie bedeutet, dass die Treiberpberprüfung eine Situation erkannt hat, in der in mindestens einem Fall LockAerworben und gehalten wird bevorLockB erworben wird, und in einem anderen Fall LockB erworben und gehalten wird, bevor LockA erforderlich ist.

Wichtig Diese Fehlerüberprüfung tritt auf, wenn die Treiberüberprüfung erkennt, dass die Hierarchieverletzung aufgetreten ist, nicht, wenn eine tatsächliche Deadlock-Situation auftritt. Dieser Verstoß erzwingt nachdrücklich, dass alle Sperren, die von den verschiedenen Komponenten eines Treibers gemeinsam genutzt werden, immer in einer Reihenfolge erworben und freigegeben werden sollten, die ein Deadlock zweier Threads unmöglich macht.

Neu in Windows 8.1 Wenn die Treiberüberprüfung auf diese Verletzung stößt, fragt der Debugger Sie nach Eingaben zu dem Fehler. In Windows 8 und früheren Versionen von Windows führt diese Verletzung zu einer sofortigen Fehlerüberprüfung.

************ 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?

Um diese Verletzung auf einem Computer unter Windows 8.1 zu debuggen, wählen Sie B (Unterbrechen) aus, und geben Sie den vorgeschlagenen Debuggerbefehl (!deadlock) ein:

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'.

Der Befehl !deadlock 3 kann auch verwendet werden, um weitere Informationen anzuzeigen, einschließlich des Stapels zum Zeitpunkt des letzten Kaufs:

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 >>

Der Debugger schlägt vor, den Kb-Befehl (Display Stack Backtrace) zum Anzeigen der aktuellen Stapelablaufverfolgung zu verwenden.

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

Die Debugger-Ausgabe zeigt, dass der betreffende Treiber diese Regel verletzt hat, indem er Sperre A erworben und gehalten hat, bevor er Sperre B auf einem Thread erworben hat, und nun Sperre B erworben hat und versucht, Sperre A auf einem anderen Thread zu erwerben. Beachten Sie, dass der erste Thread (Thread 0) beendet wird, sodass der Erwerb und die nachfolgende Freigabe dieser beiden Locks zu einem bestimmten Zeitpunkt seit dem Laden des Treiberimages erfolgt sind.

Wenn die richtigen Symbole für den Testtreiber geladen werden, zeigt der Debugger die Funktion an, in der die Sperre zum Zeitpunkt erworben wurde. In diesem Beispiel geschieht dies, dass sowohl Lock A als auch Lock B in derselben Funktion abgerufen werden. In Thread 0 werden beide in SystemControlIrpWorker erworben. In Thread 1 werden sie beide in DeviceControlIrpWorker (in der Lock B-Ausgabe von !deadlock 3 und der aktuellen Stapelausgabe (kb) angezeigt.

An diesem Punkt sollte eine Überprüfung des Quellcodes jeder Funktion zeigen, dass ein Codepfad vorhanden ist, in dem die Sperren in dieser Reihenfolge erworben werden können.

Sowohl MyTestDriver! AlphaLock und MyTestDriver! BravoLock sind Objekte, die global im Treiber verfügbar sind:

include "MyTestDriverHeader.h"

// Locks used to control access to various objects
extern KSPIN_LOCK AlphaLock;
extern KSPIN_LOCK BravoLock;

Innerhalb der Funktion SystemControlIrpWorker gibt es einen Pfad, in dem AlphaLock (Lock A in der !deadlock-Ausgabe) abgerufen und gehalten wird, wenn BravoLock (Lock B) erworben wird. Es ist auch erwähnenswert, dass die Locks in der umgekehrten Reihenfolge, in der sie erworben werden, ordnungsgemäß freigegeben werden. (Der folgende Code wird stark bearbeitet, um nur die Elemente anzuzeigen, die zum Generieren dieses Szenarios erforderlich sind).

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>>
}

Wenn Sie die folgende DeviceControlIrpWorker-Beispielfunktion überprüfen, können Sie sehen, dass es möglich ist, die Locks in umgekehrter Reihenfolge abzurufen. Das heißt, BravoLock kann erworben und gehalten werden, wenn sie versuchen, AlphaLock zu erwerben. Das folgende Beispiel ist vereinfacht, zeigt jedoch, dass es einen möglichen Pfad gibt, in dem eine Verletzung auftreten kann.

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>>
}

Um diesen potenziellen Verstoß zu beheben, wäre es richtig, sicherzustellen, dass der Treiber bei jedem Versuch, AlphaLock zu erwerben, eine Prüfung durchführt und sicherstellt, dass BravoLock nicht gehalten wird. Die einfachste Lösung könnte darin bestehen, BravoLock einfach freizugeben und erneut zu erwerben, sobald AlphaLock erworben wurde. Es können jedoch umfangreichere Codeänderungen erforderlich sein, wenn unbedingt sichergestellt werden muss, dass sich die von BravoLock geschützten Daten während des Wartens auf AlphaLock und die erneute Erfassung von BravoLock nicht ändern.

Weitere Informationen zu Spin Locks und anderen Synchronisierungstechniken finden Sie unter Spin Locks.

Spin Locks

Fehlerüberprüfung 0xC4: DRIVER_VERIFIER_DETECTED_VIOLATION

!deadlock