You definitely know that kernel stack can be outswapped if some conditions are met. One such condition is waiting with WaitMode set to UserMode.
If an event is allocated on a kernel stack it can be swapped out when a driver does something like this
GetOperationCompletionStatus( ... )
{
KEVENT Event;
KeInitializeEvent( &Event. SynchronizationEvent, FALSE );
KeAcquireSpinlock( &Lock, &OldIrql );
{
if( FALSE == Opeartion->Completed ){
Opeartion->CompletionEvent = &Event;
Wait = TRUE;
}
}
KeReleaseSpinLock( &Lock, OldIrql );
while( Wait ){
// allow a user to wake up the thread when terminating the
// process, but note that the stack might be outswapped
// when the thread is blocked waiting for the event
WaitStatus = KeWaitForSingleObject( &Event,
Executive,
UserMode,
FALSE,
NULL );
if( STATUS_SUCCESS != WaitStatus )
{
KeAcquireSpinlock( &Lock, &OldIrql );
{
// if NULL then go back to waiting as
// there is a ongoing completion
if( Opeartion->CompletionEvent ){
Opeartion->CompletionEvent = NULL;
Wait = FALSE;
}
}
KeReleaseSpinLock( &Lock, OldIrql );
}
}
NotifyOfCompletion()
{
KeAcquireSpinlock( &Lock, &OldIrql );
{
Opeartion->Completed = TRUE;
if( Opeartion->CompletionEvent ){
// the following call sometimes crashes the system
// when tries to access an outswapped page
KeSetEvent( Opeartion->CompletionEvent, IO_NO_INCREMENT,
FALSE );
Opeartion->CompletionEvent = NULL;
}
}
KeReleaseSpinLock( &Lock, OldIrql );
}
the reason to do this is when you want to be slightly more gentle and allow a user to terminate a waiting thread, this is a common scenario for distributed file systems where response time might be up to minutes.
The problem with the above code is that a kernel stack can be swapped out while waiting in KeWaitForSingleObject whith waiting mode set to UserMode . The call to KeSetEvent tries to access the event on the outswapped stack when the IRQL is DISPATCH_LEVEL and it has nothing to do with a call to KeAcquireSpinlock, the same will be even if you try to call KeSetEvent without raising IRQL as KeSetEvent elevates IRQL when working with the event.
If you check the event address on an outswapped stack with WinDBG you see
1: kd> !pte 0xaf792cac
VA af792cac
PDE at C0602BD8 PTE at C057BC90
contains 000000005402F863 contains 00000000A5129BE2
pfn 5402f ---DA--KWEV not valid
Transition: a5129
Protect: 1f - Outswapped kernel stack
The solution to the above example is to allocate the event from the NonPaged pool.
If an event is allocated on a kernel stack it can be swapped out when a driver does something like this
GetOperationCompletionStatus( ... )
{
KEVENT Event;
KeInitializeEvent( &Event. SynchronizationEvent, FALSE );
KeAcquireSpinlock( &Lock, &OldIrql );
{
if( FALSE == Opeartion->Completed ){
Opeartion->CompletionEvent = &Event;
Wait = TRUE;
}
}
KeReleaseSpinLock( &Lock, OldIrql );
while( Wait ){
// allow a user to wake up the thread when terminating the
// process, but note that the stack might be outswapped
// when the thread is blocked waiting for the event
WaitStatus = KeWaitForSingleObject( &Event,
Executive,
UserMode,
FALSE,
NULL );
if( STATUS_SUCCESS != WaitStatus )
{
KeAcquireSpinlock( &Lock, &OldIrql );
{
// if NULL then go back to waiting as
// there is a ongoing completion
if( Opeartion->CompletionEvent ){
Opeartion->CompletionEvent = NULL;
Wait = FALSE;
}
}
KeReleaseSpinLock( &Lock, OldIrql );
}
}
NotifyOfCompletion()
{
KeAcquireSpinlock( &Lock, &OldIrql );
{
Opeartion->Completed = TRUE;
if( Opeartion->CompletionEvent ){
// the following call sometimes crashes the system
// when tries to access an outswapped page
KeSetEvent( Opeartion->CompletionEvent, IO_NO_INCREMENT,
FALSE );
Opeartion->CompletionEvent = NULL;
}
}
KeReleaseSpinLock( &Lock, OldIrql );
}
the reason to do this is when you want to be slightly more gentle and allow a user to terminate a waiting thread, this is a common scenario for distributed file systems where response time might be up to minutes.
The problem with the above code is that a kernel stack can be swapped out while waiting in KeWaitForSingleObject whith waiting mode set to UserMode . The call to KeSetEvent tries to access the event on the outswapped stack when the IRQL is DISPATCH_LEVEL and it has nothing to do with a call to KeAcquireSpinlock, the same will be even if you try to call KeSetEvent without raising IRQL as KeSetEvent elevates IRQL when working with the event.
If you check the event address on an outswapped stack with WinDBG you see
1: kd> !pte 0xaf792cac
VA af792cac
PDE at C0602BD8 PTE at C057BC90
contains 000000005402F863 contains 00000000A5129BE2
pfn 5402f ---DA--KWEV not valid
Transition: a5129
Protect: 1f - Outswapped kernel stack
The solution to the above example is to allocate the event from the NonPaged pool.
No comments:
Post a Comment