この記事は、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, :
Microsoft whitepaper( ) , :
MSDN Managing Hardware Priorities
, - - - Api, IRQL, WorkItem’. , .