Windowsカーネルドライバー-一般的なエラー-IRQL

この記事は、Windows用のカーネルドライバーの開発を始めたばかりの人を対象としています。嫌われているIRQL_NOT_LESS_OR_EQUALとこの悲しい笑顔を100回目にしますか?それなら猫の下に行ってください。





私自身が犯した主な間違いの1つは、IRQLを思い通りにジャグリングすることと、Windowsカーネルのスレッド優先順位の内部動作を完全に理解していないことです。



たとえば、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





Microsoft whitepaper( ) , :





MSDN Managing Hardware Priorities





, - - - Api, IRQL, WorkItem’. , .








All Articles