Drivers do kernel do Windows - Erros comuns - IRQL

Este artigo é direcionado àqueles que acabaram de começar a desenvolver drivers de kernel para Windows. Pela centésima vez você vê o odiado IRQL_NOT_LESS_OR_EQUAL e esta carinha triste? Então, por favor, vá por baixo do gato.





Um dos principais erros que eu mesmo cometi é fazer malabarismos com o IRQL da maneira que você deseja e uma compreensão incompleta do funcionamento interno das prioridades de thread no kernel do Windows.



Por exemplo, você tem um trecho de código que gera um evento para um processo PID.





KSPIN_LOCK SharedDataLock;

NTSTATUS SomeFunction(_In_ HANDLE ProcessId)
{
    KIRQL OriginalIrql;

    NTSTATUS status = STATUS_UNSUCCESSFUL;

    // lock shared data access
    KeAcquireSpinLock(&SharedDataLock, &OriginalIrql);

    // some code, which works with shared data
    PVOID data = GetProcessSharedData(ProcessId);
    NT_ASSERT("GetProcessSharedData() must always return shared data.", data);

    PEPROCESS Process = NULL;
    // next we need to get process image name
    status = PsLookupProcessByProcessId(
									ProcessId,
									&Process
                                );

    if ( !NT_SUCCESS( status ) )
    {
        goto LOCK_RELEASE;
    }

    PUNICODE_STRING ProcessImage = NULL;
    status = GetProcessImagename(Process, &ProcessImage);
    if ( !NT_SUCCESS( status ) )
    {
        goto PROCESS_LINK_DEREF;
    }

    // generate some event for our log
    GenerateEvent( ProcessImage, data );

PROCESS_LINK_DEREF:
	ObDereferenceObject(Process);
LOCK_RELEASE:     // release lock     
	KeReleaseSpinLock(&SharedDataLock, OriginalIrql);
	return status;
}
      
      



, , , . , .





?





PsLookupProcessByProcessId()



– : IRQL <= APC_LEVEL.





, BSOD IRQL_NOT_LESS_OR_EQUAL.





, . , , IRQL , .





, — :





KSPIN_LOCK SharedDataLock;

VOID SetIrql(_In_ KIRQL Irql)
{
	if ( KeGetCurrentIrql() > Irql )
		KeLowerIrql(Irql);
	else if ( KeGetCurrentIrql() < Irql )
		KzRaiseIrql(Irql);
}

NTSTATUS SomeFunction(_In_ HANDLE ProcessId)
{
    KIRQL OriginalIrql;

    NTSTATUS status = STATUS_UNSUCCESSFUL;

    // lock shared data access
    KeAcquireSpinLock(&SharedDataLock, &OriginalIrql);

    // some code, which works with shared data
    PVOID data = GetProcessSharedData(ProcessId);
    NT_ASSERT("GetProcessSharedData() must always return shared data.", data);

    SetIrql(APC_LEVEL);

    PEPROCESS Process = NULL;
    // next we need to get process image name
    status = PsLookupProcessByProcessId(
                                     ProcessId,
                                     &Process
                                );

    SetIrql(DISPATCH_LEVEL);

    if ( !NT_SUCCESS( status ) )
    {
        goto LOCK_RELEASE;
    }

    PUNICODE_STRING ProcessImage = NULL;
    status = GetProcessImagename(Process, &ProcessImage);
    if ( !NT_SUCCESS( status ) )
    {
        goto PROCESS_LINK_DEREF;
    }

    // generate some event for our log
    GenerateEvent( ProcessImage, data );

PROCESS_LINK_DEREF:
	ObDereferenceObject(Process);
LOCK_RELEASE:
  // release lock     
	KeReleaseSpinLock(&SharedDataLock, OriginalIrql);
	return status;
}
      
      



, . , . , , 1 1000 , , , .





, :

« IRQL , , !»





:





- IRQL = APC_LEVEL, . IRQL DISPATCH_LEVEL, APC_LEVEL, .





, , :





KSPIN_LOCK SharedDataLock;

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS SomeFunction(_In_ HANDLE ProcessId)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;

    PEPROCESS Process = NULL;
    // next we need to get process image name
    status = PsLookupProcessByProcessId(
                                   ProcessId,
                                   &Process
                                );

    if ( !NT_SUCCESS( status ) )
    {
        goto EXIT_ROUTINE;
    }

    PUNICODE_STRING ProcessImage = NULL;
    status = GetProcessImagename(Process, &ProcessImage);
    if ( !NT_SUCCESS( status ) )
    {
        goto PROCESS_LINK_DEREF;
    }

    // lock shared data access
    KeAcquireSpinLock(&SharedDataLock, &OriginalIrql);

    // some code, which works with shared data
    PVOID data = GetProcessSharedData(ProcessId);
    NT_ASSERT("GetProcessSharedData() must always return shared data.", data);

    // generate some event for our log
    GenerateEvent( ProcessImage, data );

    // release lock
    KeReleaseSpinLock(&SharedDataLock, OriginalIrql);
		
PROCESS_LINK_DEREF:
	ObDereferenceObject(Process);
EXIT_ROUTINE:
	return status;
}	
      
      



SetIrql() 2- , , .. , .





, SAL, :





MSDN SAL 2.0





A Microsoft também fornece um pequeno white paper (no final do artigo) sobre gerenciamento de prioridade de thread no kernel e conta com mais detalhes algumas das sutilezas de trabalhar com eles:





MSDN Gerenciando Prioridades de Hardware





Se, no entanto, você ainda precisar chamar alguma API que exija valores de IRQL mais baixos, os WorkItems podem se tornar uma das opções para resolver esse problema. Mas falarei sobre eles em outro artigo.








All Articles