Windows Kernel Drivers - Common Errors - IRQL

This article is aimed at those who have just started developing kernel drivers for Windows. For the 100th time you see the hated IRQL_NOT_LESS_OR_EQUAL and this sad smiley face? Then please go under the cat.





One of the main mistakes that I myself made is juggling IRQL the way you want, and incomplete understanding of the inner workings of thread priorities in the Windows kernel.



For example, you have a piece of code that generates an event for a PID process.





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





Microsoft also provides a small whitepaper (at the very bottom of the article) on thread priority management in the kernel, and tells in more detail some of the subtleties of working with them:





MSDN Managing Hardware Priorities





If, however, you still need to somehow call any Api that requires lower IRQL values, then WorkItems can become one of the options for solving this problem. But I will talk about them in another article.








All Articles