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, :
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.