Поделиться через


Отладка взаимоблокировок — DRIVER_VERIFIER_DETECTED_VIOLATION (C4): 0x1001

Когда средство проверки драйверов обнаруживает нарушение иерархии блокировки спина, проверка ошибок проверки драйвера проверяет 0xC4: DRIVER_VERIFIER_DETECTED_VIOLATION со значением 1 значения 0x1001.

Если параметр обнаружения взаимоблокировок активен (обнаружение взаимоблокировки входит в состав стандартных параметров средства проверки драйверов), средство проверки драйверов отслеживает каждый выделенный блок спина и порядок, в котором он был приобретен и освобожден. Нарушение иерархии блокировки означает, что средство проверки драйверов обнаружило ситуацию, когда, по крайней мере в одном случае, lockA приобретается и удерживается до приобретения LockB, а в другом случае lockB приобретается и удерживается до того, как lockA требуется.

Важно, что эта ошибка проверяется всякий раз, когда средство проверки драйверов обнаруживает, что нарушение иерархии произошло, а не при возникновении фактической ситуации взаимоблокировки. Это нарушение применяет строгое принудительное применение любых блокировок, совместно используемых между различным компонентом драйвера, всегда должны быть приобретены и освобождены в порядке, что делает взаимоблокировку двух потоков невозможной.

Новое в Windows 8.1 , когда средство проверки драйверов обнаруживает это нарушение, если отладчик подключен, отладчик запрашивает входные данные об ошибке. В Windows 8 и предыдущих версиях Windows это нарушение приведет к немедленной проверке ошибок.

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

Чтобы выполнить отладку этого нарушения на компьютере под управлением Windows 8.1, выберите B (Разрыв) и введите предложенную команду отладчика (!взаимоблокировка):

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

Команда !взаимоблокировки 3 также может использоваться для отображения дополнительных сведений, включая стек во время последнего получения:

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

Отладчик предлагает использовать команду kb (Display Stack Backtrace) для отображения текущей трассировки стека.

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

Выходные данные отладчика показывают, что драйвер, под вопросом, нарушил это правило, получив и удерживая блокировку A перед получением блокировки B на одном потоке, и теперь приобрел блокировку B и пытается получить lock A на другом потоке. Обратите внимание, что первый поток (Thread 0) завершается, поэтому приобретение и последующее освобождение этих двух блокировок произошло в какой-то момент после загрузки образа драйвера.

При загрузке соответствующих символов для тестового драйвера отладчик отобразит функцию, в которой была получена блокировка в то время. В этом примере так происходит, что в одной функции получаются как блокировка A, так и блокировка B. В Thread 0 оба они приобретены в SystemControlIrpWorker. В Потоке 1 они оба получаются в DeviceControlIrpWorker (показана в выходных данных Lock B из !взаимоблокировки 3 и текущего выходных данных стека (кб).

На этом этапе проверка исходного кода каждой функции должна показать, что путь к коду существует, где блокировки можно получить в таком порядке.

Оба MyTestDriver! AlphaLock и MyTestDriver! BravoLock — это объекты глобально доступны в драйвере:

include "MyTestDriverHeader.h"

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

Внутри функции SystemControlIrpWorker существует путь, в котором альфаблокировка (блокировка A в выходных данных !взаимоблокировки ) приобретается и удерживается при получении BravoLock (Lock B). Также стоит отметить, что блокировки правильно освобождены в обратном порядке, в котором они приобретены. (Следующий код сильно редактируется, чтобы отобразить только элементы, необходимые для создания этого сценария.

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

При просмотре следующей функции DeviceControlIrpWorker можно увидеть, что можно получить блокировки в обратном порядке. То есть, BravoLock можно получить и сохранить при попытке получить AlphaLock. В следующем примере упрощено, но показано, что существует возможный путь, в котором может произойти нарушение.

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

Чтобы устранить это потенциальное нарушение, правильно сделать будет убедиться, что всякий раз, когда водитель пытается получить AlphaLock, он проверяет и гарантирует, что BravoLock не проводится. Самое простое исправление может быть просто освободить BravoLock и повторно приобрести его сразу после приобретения AlphaLock. Но более значительные изменения кода могут потребоваться, если важно, чтобы любые данные BravoLock не изменялись во время ожидания AlphaLock и повторного приобретения BravoLock.

Дополнительные сведения о блокировках спина и других методах синхронизации см. в разделе "Замки спина".

Замки спина

Проверка ошибок 0xC4: DRIVER_VERIFIER_DETECTED_VIOLATION

!тупик