Request (IRP) cancellation in Network MiniRedirector

I’m working on a Network MiniRedirector using the RDBSS framework. I’m trying to enable Request/IRP cancellation logic in my driver but I’m having trouble getting RDBSS to invoke my cancellation routine. On entry of a write request, I called RxSetMinirdrCancelRoutine(RxContext, MyCancelRoutine). Then pass the request to an sync/async handler depending on the RX_CONTEXT_FLAG_ASYNC_OPERATION. I setup a test case such that the network becomes unresponsive and my driver has to keep on retrying the write request. I assume that when user press Ctrl+C, RDBSS, or the IO manager would call the cancel routine I registered, but that did not happened.

If I directly use IoRegisterCancelRoutine with the RxContext->CurrentIrp to register my cancel routine, the IOManager will call the cancel routine when Ctrl+C is executed. However, RDBSS documentation suggests that we should not directly use the IRP associated with the RxContext.

The SMBMRx sample code also uses RxSetMinirdrCancelRoutine to register the cancel routine. I noticed it was making use of the fields RX_CONTEXT -> MrxContext, so I tried to set those field to Non-NULL values but my cancellation routine is not getting invoked.

There’s so little documentation on this subject to go on. I searched over the MINIRDR_DISPATCH structure but I did not find any flags that I needed to set. Inside MINIRDR_DISPATCH, there is a field called MrxCancel, but the documentation and the SMBMRX sample code driver indicate that it is not used and should be set to NULL, which I did.

Has anyone successfully make use of RxSetMinirdrCancelRoutine() and was able to get RDBSS or IOManager to invoke the cancellation routine in responding to Ctrl+C?

Any pointer would be much appreciated.

Upon further inspection, I noticed that RDBSS calls IoSetCancelRoutine and set the IRP -> CancelRoutine field to point to RxCancelRoutine() function. I placed a breakpoint on RxCancelRoutine() and kept the original request pending (I return STATUS_PENDING to the original request but has not yet complete it). When I pressed Ctrl+C, the breakpoint did not hit. Something is preventing the cancellation routine from getting invoked.

With additional debugging, here is a better picture of what is happening:

  • Main thread (T1) sends a write from usermode and that request gets to my driver.
  • At this point the IRP’s CancelRoutine is set to my cancellation routine.
  • Still running under (T1) context, I posted the work to a worker thread (Tw) and waited for the work to be completed by the worker thread. So (T1) went into an alertable wait state KeWaitForSingleObject(waitevent, Executive, KernelMode, TRUE, NULL). I made sure that normal APC is NOT disabled by making sure the KernelApcDisable, and SpecialApcDisable fields in _KTHREAD structure are both 0.
  • (Tw) is busy so it won’t complete the work.
  • I issued Ctrl + C.
  • There is a new thread that is processing the cancellation Ctrl +C (Tc). Following is its callstack.
    nt!KiInsertQueueApc+0x20b
    nt!KeInsertQueueApc+0x80
    nt!IopCancelIrpsInThreadList+0x60
    nt!IopCancelSynchronousIrpsForThread+0x68
    nt!NtCancelSynchronousIoFile+0x98
    nt!KiSystemServiceCopyEnd+0x13
    ntdll!ZwCancelSynchronousIoFile+0xa
    kernel32!CancelSynchronousIo+0x11
  • This new thread (Tc) tries to update the KTHREAD->ApcState->ApcListHead of (T1). I would assume that (Tc) is queuing a Kernel APC so (T1) would need to execute.
  • When (T1) is processing the wait, it reset the KTHREAD->ApcState->ApcListHead that was set in the previous step.

I assume that the APC processing logic would eventually call the CancelRoutine registered in the IRP but it was never invoked. I place NT_ASSERT(FALSE) in the function but it never hit.

Is it correct for me to expect that (T1) would get preempted from the wait state and switch over to process the APC before clearing its KTHREAD->ApcState->ApcListHead?

Thanks!

If you look at the thread you’re expecting to have the I/O cancelled, can you tell what the WaitIrql is ("dt nt!_KTHREAD

" will show you this field)?

Tony
OSR

WaitIrql for the main thread (T1) is 0

kd> dt nt!_KTHREAD fffffa80`02f77b60
+0x000 Header : _DISPATCHER_HEADER
+0x018 CycleTime : 0x4cfc0963

+0x165 NpxState : 1 ‘’
+0x166 WaitIrql : 0 ‘’ <---------------------
+0x167 WaitMode : 0 ‘’
+0x108 WaitBlockFill6 : [140] “???”
+0x194 WaitTime : 0x1e9a9
+0x108 WaitBlockFill7 : [168] “???”
+0x1b0 TebMappedLowVa : (null)
+0x1b8 Ucb : (null)

By the way, this is the stacktrace of the the main thread (T1). The breakpoint was hit when the Apc was being delivered.

nt!KiDeliverApc+0x18a
nt!KiCommitThreadWait+0x3dd
nt!KeWaitForSingleObject+0x19f
nt!VerifierKeWaitForSingleObject+0x151
phr!PhrMrxLowIoWrite+0x1f
phr!RxLowIoSubmit+0x2a9 [d:\5359\base\fs\rdr2\rxce\lowio.c @ 809]
phr!RxLowIoWriteShell+0x99 [d:\5359\base\fs\rdr2\rdbss\write.c @ 2098]
phr!RxCommonWrite+0x1c2e [d:\5359\base\fs\rdr2\rdbss\write.c @ 1508]
phr!RxFsdCommonDispatch+0x49f [d:\5359\base\fs\rdr2\rdbss\ntfsd.c @ 848]
phr!RxFsdDispatch+0xe4 [d:\5359\base\fs\rdr2\rdbss\ntfsd.c @ 442]
nt!IovCallDriver+0x566
mup!MupiCallUncProvider+0x161
mup!MupStateMachine+0x128
mup!MupFsdIrpPassThrough+0x12d
nt!IovCallDriver+0x566
fltmgr!FltpLegacyProcessingAfterPreCallbacksCompleted+0x24f
fltmgr!FltpDispatch+0xcf
nt!IovCallDriver+0x566
nt!IopSynchronousServiceTail+0xfb
nt!NtWriteFile+0x7e2
nt!KiSystemServiceCopyEnd+0x13
ntdll!ZwWriteFile+0xa
KERNELBASE!WriteFile+0xfe
kernel32!BaseCopyStream+0x4b2
kernel32!BasepCopyFileExW+0x545
kernel32!CopyFileExW+0x97
cmd!do_normal_copy+0x95a
cmd!copy+0x152
cmd!eCopy+0xd
cmd!FindFixAndRun+0x2ad
cmd!Dispatch+0x192
cmd!main+0x454
cmd!LUAGetUserType+0x315
kernel32!BaseThreadInitThunk+0xd
ntdll!RtlUserThreadStart+0x1d

So you are getting the cancellation APC - it’s not being blocked, but your cancel routine is not being called?

Tony
OSR

That is correct. I am getting cancellation APC, it is not blocked, but my cancel routine is not being called. This is the content of the APC

kd> dt nt!_KAPC (0xfffff88002147b40 - 0x10) +0x000 Type : 0x12 '' +0x001 SpareByte0 : 0 '' +0x002 Size : 0x58 'X' +0x003 SpareByte1 : 0 '' +0x004 SpareLong0 : 0 +0x008 Thread : 0xfffffa8002f77b60 _KTHREAD
+0x010 ApcListEntry : _LIST_ENTRY [0xfffffa8002f77bb0 - 0xfffffa8002f77bb0]
+0x020 KernelRoutine : 0xfffff800028d28f0 void nt!IopCancelIrpsInCurrentThreadListSpecialApc+0 +0x028 RundownRoutine : (null) +0x030 NormalRoutine : (null) +0x038 NormalContext : (null) +0x040 SystemArgument1 : 0xfffff88002147b10 Void
+0x048 SystemArgument2 : (null)
+0x050 ApcStateIndex : 0 ‘’
+0x051 ApcMode : 0 ‘’
+0x052 Inserted : 0x1 ‘’

So, I’m trying to look for the IRP list from KTHREAD structure to make sure my IRP is in there but I couldn’t find the field in KTHREAD that would lead me to the list of IRP associated with the current thread. The field _KTHREAD->ThreadListEntry is actually an entry of the linked list of threads.

Sorry for spamming but I was hoping there is something I missed in the _KTHREAD structure.

kd> dt nt!_KTHREAD fffffa8002f77b60 +0x000 Header : _DISPATCHER_HEADER +0x018 CycleTime : 0x4cfc0963 +0x020 QuantumTarget : 0x5e87fd14 +0x028 InitialStack : 0xfffff88005b17db0 Void
+0x030 StackLimit : 0xfffff88005b0f000 Void +0x038 KernelStack : 0xfffff88005b16eb0 Void
+0x040 ThreadLock : 0
+0x048 WaitRegister : _KWAIT_STATUS_REGISTER
+0x049 Running : 0x1 ‘’
+0x04a Alerted : [2] “”
+0x04c KernelStackResident : 0y1
+0x04c ReadyTransition : 0y0
+0x04c ProcessReadyQueue : 0y0
+0x04c WaitNext : 0y0
+0x04c SystemAffinityActive : 0y0
+0x04c Alertable : 0y1
+0x04c GdiFlushActive : 0y0
+0x04c UserStackWalkActive : 0y0
+0x04c ApcInterruptRequest : 0y0
+0x04c ForceDeferSchedule : 0y0
+0x04c QuantumEndMigrate : 0y0
+0x04c UmsDirectedSwitchEnable : 0y0
+0x04c TimerActive : 0y0
+0x04c SystemThread : 0y0
+0x04c Reserved : 0y000000000000000000 (0)
+0x04c MiscFlags : 0n33
+0x050 ApcState : _KAPC_STATE
+0x050 ApcStateFill : [43] “???”
+0x07b Priority : 10 ‘’
+0x07c NextProcessor : 0
+0x080 DeferredProcessor : 0
+0x088 ApcQueueLock : 1
+0x090 WaitStatus : 0n256
+0x098 WaitBlockList : 0xfffffa8002f77c68 _KWAIT_BLOCK +0x0a0 WaitListEntry : _LIST_ENTRY [0xfffffa8002681100 - 0xfffff800029fe420] +0x0a0 SwapListEntry : _SINGLE_LIST_ENTRY +0x0b0 Queue : (null) +0x0b8 Teb : 0x000007fffffde000 Void
+0x0c0 Timer : _KTIMER
+0x100 AutoAlignment : 0y0
+0x100 DisableBoost : 0y0
+0x100 EtwStackTraceApc1Inserted : 0y0
+0x100 EtwStackTraceApc2Inserted : 0y0
+0x100 CalloutActive : 0y0
+0x100 ApcQueueable : 0y1
+0x100 EnableStackSwap : 0y1
+0x100 GuiThread : 0y1
+0x100 UmsPerformingSyscall : 0y0
+0x100 VdmSafe : 0y0
+0x100 UmsDispatched : 0y0
+0x100 ReservedFlags : 0y000000000000000000000 (0)
+0x100 ThreadFlags : 0n224
+0x104 Spare0 : 0
+0x108 WaitBlock : [4] _KWAIT_BLOCK
+0x108 WaitBlockFill4 : [44] “???”
+0x134 ContextSwitches : 0xa2a
+0x108 WaitBlockFill5 : [92] “???”
+0x164 State : 0x2 ‘’
+0x165 NpxState : 1 ‘’
+0x166 WaitIrql : 0 ‘’
+0x167 WaitMode : 0 ‘’
+0x108 WaitBlockFill6 : [140] “???”
+0x194 WaitTime : 0x1e9a9
+0x108 WaitBlockFill7 : [168] “???”
+0x1b0 TebMappedLowVa : (null)
+0x1b8 Ucb : (null)
+0x108 WaitBlockFill8 : [188] “???”
+0x1c4 KernelApcDisable : 0n0
+0x1c6 SpecialApcDisable : 0n0
+0x1c4 CombinedApcDisable : 0
+0x1c8 QueueListEntry : _LIST_ENTRY [0x0000000000000000 - 0x0000000000000000]
+0x1d8 TrapFrame : (null)
+0x1e0 FirstArgument : 0x000000000000005c Void +0x1e8 CallbackStack : (null) +0x1e8 CallbackDepth : 0 +0x1f0 ApcStateIndex : 0 '' +0x1f1 BasePriority : 8 '' +0x1f2 PriorityDecrement : 2 '' +0x1f2 ForegroundBoost : 0y0010 +0x1f2 UnusualBoost : 0y0000 +0x1f3 Preempted : 0 '' +0x1f4 AdjustReason : 0 '' +0x1f5 AdjustIncrement : 0 '' +0x1f6 PreviousMode : 1 '' +0x1f7 Saturation : 0 '' +0x1f8 SystemCallNumber : 5 +0x1fc FreezeCount : 0 +0x200 UserAffinity : _GROUP_AFFINITY +0x210 Process : 0xfffffa8002f71570 _KPROCESS
+0x218 Affinity : _GROUP_AFFINITY
+0x228 IdealProcessor : 0
+0x22c UserIdealProcessor : 0
+0x230 ApcStatePointer : [2] 0xfffffa8002f77bb0 _KAPC_STATE +0x240 SavedApcState : _KAPC_STATE +0x240 SavedApcStateFill : [43] "???" +0x26b WaitReason : 0 '' +0x26c SuspendCount : 0 '' +0x26d Spare1 : 0 '' +0x26e CodePatchInProgress : 0 '' +0x270 Win32Thread : 0xfffff900c26b45e0 Void
+0x278 StackBase : 0xfffff88005b18000 Void +0x280 SuspendApc : _KAPC +0x280 SuspendApcFill0 : [1] "??????" +0x281 ResourceIndex : 0x1 '' +0x280 SuspendApcFill1 : [3] "???" +0x283 QuantumReset : 0x12 '' +0x280 SuspendApcFill2 : [4] "???" +0x284 KernelTime : 0xdf +0x280 SuspendApcFill3 : [64] "???" +0x2c0 WaitPrcb : (null) +0x280 SuspendApcFill4 : [72] "???" +0x2c8 LegoData : (null) +0x280 SuspendApcFill5 : [83] "???" +0x2d3 LargeStack : 0x1 '' +0x2d4 UserTime : 0 +0x2d8 SuspendSemaphore : _KSEMAPHORE +0x2d8 SuspendSemaphorefill : [28] "???" +0x2f4 SListFaultCount : 0 +0x2f8 ThreadListEntry : _LIST_ENTRY [0xfffffa800270e668 - 0xfffffa8002f715a0] +0x308 MutantListHead : _LIST_ENTRY [0xfffffa8002f77e68 - 0xfffffa80`02f77e68]
+0x318 SListFaultAddress : (null)
+0x320 ReadOperationCount : 0n278
+0x328 WriteOperationCount : 0n287
+0x330 OtherOperationCount : 0n1436
+0x338 ReadTransferCount : 0n260611275
+0x340 WriteTransferCount : 0n259557067
+0x348 OtherTransferCount : 0n24508
+0x350 ThreadCounters : (null)
+0x358 XStateSave : (null)

Thank you for your help, Tony!

So, the IRP that I am trying to cancel I/O on is at 0xfffff980063e6e10, and it is in the IRP list of the current thread.

kd> ?? @$thread->IrpList
struct _LIST_ENTRY
[0xfffff980063e6e30 - 0xfffff980063e6e30]
+0x000 Flink : 0xfffff980063e6e30 _LIST_ENTRY [0xfffffa8002f77f48 - 0xfffffa8002f77f48] +0x008 Blink : 0xfffff980063e6e30 _LIST_ENTRY [0xfffffa8002f77f48 - 0xfffffa8002f77f48]

kd> dt nt!_IRP (0xfffff980063e6e30 - 0x20) +0x000 Type : 0n6 +0x002 Size : 0x1f0 +0x008 MdlAddress : 0xfffff9800296e7c0 _MDL
+0x010 Flags : 0x40060a00
+0x018 AssociatedIrp :
+0x020 ThreadListEntry : _LIST_ENTRY [0xfffffa8002f77f48 - 0xfffffa8002f77f48]
+0x030 IoStatus : _IO_STATUS_BLOCK
+0x040 RequestorMode : 1 ‘’
+0x041 PendingReturned : 0 ‘’
+0x042 StackCount : 4 ‘’
+0x043 CurrentLocation : 2 ‘’
+0x044 Cancel : 0 ‘’
+0x045 CancelIrql : 0 ‘’
+0x046 ApcEnvironment : 0 ‘’
+0x047 AllocationFlags : 0x81 ‘’
+0x048 UserIosb : 0x000000000029ba00 _IO_STATUS_BLOCK<br> +0x050 UserEvent : 0xfffffa8002b177f0 _KEVENT
+0x058 Overlay :
+0x068 CancelRoutine : 0xfffff88005c52950 void phr!PhrIrpCancelRoutine+0<br> +0x070 UserBuffer : 0x0000000002780040 Void
+0x078 Tail :

It’s not a kernel structure, it’s an I/O Manager structure so it is in the ETHREAD:

“dt nt!_ETHREAD ”

Look for IrpThreadList

Tony
OSR

So this IRP hasn’t been cancelled.

But it is in a cancellable state.

Is this routine NOT getting called? Presumably, the APC should be calling this. Can you confirm that the global cancel spinlock isn’t held by some other CPU? It seems very unlikely, but it’s worth verifying.

At this point, I’d set a breakpoint in the APC routine and see if you can walk the code to figure out why it isn’t calling this cancel routine.

Tony
OSR

The cancel routine “phr!PhrIrpCancelRoutine” is not getting called. I only had an NT_ASSERT(FALSE) in the definition of that function and it didn’t hit.

I will verify that the global cancel spinlock is not held by other CPU.

Then I will walk the KiDeliverAPC (the main thread T1) as well as the actual APC code path (Tc) t better understand the problem. I will update the thread when I find additional information.

> - Still running under (T1) context, I posted the work to a worker thread (Tw) and waited for the work

to be completed by the worker thread. So (T1) went into an alertable wait state
KeWaitForSingleObject

Very bad idea.

Do not wait in dispatch routines of the FS path.

At all. Return STATUS_PENDING instead.

Use IoIsOperationSynchronous to determine whether the request can be posted to a worker thread. If IoIsOperationSynchronous is TRUE - then you cannot post.

This occurs, for instance, for non-overlapped FOs and “inherently synchronous” stuff (like creates on pre-Vista, and some SetInfos).

You can also add your own logic to the “postable” requests with IoIsOperationSynchronous == FALSE and declare some of them “non-postable” due to this.

If some request is “non-postable” - then, and only then, you can use waits in dispatch path. Non-postable just means - dispatch path is allowed to block.

But, if some request is “postable” and you are posting it to the worker thread - then PLEASE do not wait in the dispatch routine. Just return STATUS_PENDING from it instead.

The worker thread will complete the request later.

Also, do not forget to IoMarkIrpPending before posting the request.


Maxim S. Shatskih
Microsoft MVP on File System And Storage
xxxxx@storagecraft.com
http://www.storagecraft.com

This is NOT true. If this returns TRUE it indicates that the caller will BLOCK waiting for the completion of the I/O, but it does NOT mean you cannot post. The I/O subsystem is *inherently* asynchronous. You may return STATUS_PENDING for any operation. There’s even a verifier setting that forces this to happen for every I/O operation (to catch drivers that assume “synchronous I/O” means MANDATORY. It does not.)

Note: STATUS_PENDING is a success code (0x103) and some calls treat it’s return as a success state (IRP_MJ_SHUTDOWN, as I recall does this).

Some I/O operations cannot be implemented synchronously - they are inherently asynchronous. But aside from those, most I/O operations can be completed synchronously.

The issue in this case would be why the work thread doesn’t run. It’s VERY easy to run into locking problems in situations like this. But like any stuck thread situation, the real key is to figure out why the threads aren’t making forward progress.

Tony
OSR

Max, and Tony, Thank you both for your input.
So I followed the code from doxygen.reactos.org for the function KiDeliverApc.
Using the code as a reference and walk through the assembly. I got to the function
nt!IopCancelIrpsInCurrentThreadListSpecialApc.

Following is my interpretation of that function. The (Ln) locations are marked so I can ask questions at the end.

nt!IopCancelIrpsInCurrentThreadListSpecialApc

    • Load register with _KAPC -> SystemArgument1 (0xfffff880047e6b10) Don't know what structure this is but the first quadword is 0xfffffa8002f77b60 ThreadAddress
    • Load register with address of thread where we want to cancel the IRP (0xfffffa80`02f77b60 struct _ETHREAD*)
    • Load register with address of _KAPC object (0xfffff880`047e6b30 struct _KAPC *)
    • store the SystemArgument1 into local variable pointer
    • Move 0 into byte ptr [rsp + 0x30]
    • MOve 1 into qword ptr [rsp + 0x98]
    • Move 2 into eax
    • Load pointer of nt!KiInitialPCR + 0x180 into @rsi
    • Check if bit 0x10 is set on nt!PerfGlobalGroupMask + 0x4
    • add to nt!KiInitialPCR + 0x4C80 , the resulted word is 03881c11
    • bit test and set atomic (lock bts) - current is 0 @ addr 0xfffffa8002f77fe0 - after set it is 1.
      0xfffffa8002f77fe0 is the IrpListLock so we acquired the IrpListLock
    • load the ETHREAD->IrpList at 0xfffffa8002f77f48 - - The entry is 0xfffff980063e6e30 (@rdx)
    • The SystemArgument1 points to a buffer that contains the address of the current thread
      There is an instruction that set the least significant byte to 0. So the value
      fffffa8002f77b[60] was set to fffffa8002f77b[00]. <------ Don’t know if that has any impact
    • Check if the IrpList is empty
    • Check the stack count
      (L1) move IRP->StackCount (4) into ecx (4)
      move IRP->CurrentLocation (2) into eax (2)
      add 2 to ecx - so ecx becomes (6)
    • Move a quadword from (rbp + 0x60) which is 0xfffff880047e6b90 into @rax, \*\*\*\*\*Notes: the address 0xfffff880047e6b90 is 7 bytes from the last bytes in the _KAPC*, yet does not seem to be a valid _KAPC. I don’t know what it is.
      kd> dt nt!_KAPC (0xfffff880047e6b90) +0x000 Type : 0 '' +0x001 SpareByte0 : 0 '' +0x002 Size : 0 '' +0x003 SpareByte1 : 0 '' +0x004 SpareLong0 : 0 +0x008 Thread : 0x0000000000060000 _KTHREAD
      +0x010 ApcListEntry : _LIST_ENTRY [0xfffffa8002f28c68 - 0xfffffa8002f28c68]
      +0x020 KernelRoutine : 0x0000000000000001 void +1 +0x028 RundownRoutine : 0x0000000000000018 void +18
      +0x030 NormalRoutine : 0xfffffa8002f77b60 void +fffffa8002f77b60 +0x038 NormalContext : 0xfffff80002caef08 Void
      +0x040 SystemArgument1 : (null)
      +0x048 SystemArgument2 : 0xfffff880`047e6ca0 Void
      +0x050 ApcStateIndex : 16 ‘’
      +0x051 ApcMode : 0 ‘’
      +0x052 Inserted : 0 ‘’

(L2) Also, the value at (rbp + 0x60) (0xfffff880`047e6b90) is 0 <------- This looks like a few bytes after the KAPC structure. I dont know what is there
Test to see whether we should jump
we did not jump

(L3) - - Move the quadword from (rbp + 0x58) which is 0xfffff880`047e6b88 into @rax,
The value at this address is also 0.
Test and we JUMPED.

(L4)- - Test the IRP->Flags (0x40060a00) for (0x0A) (#define IRP_ASSOCIATED_IRP 0x00000008 + #define IRP_PAGING_IO 0x00000002)
Bits were not set so we did not jump.
(L5) - - Test the IRP->Flags ((0x40060a00) for (0x84) (#define IRP_CREATE_OPERATION 0x00000080 + #define IRP_SYNCHRONOUS_API 0x00000004)
jne We did not jump.

    • Test the Irp->AllocationFlags (0x81) to see if (0x2) flag (#define IRP_ALLOCATED_MUST_SUCCEED 0x02) is set
    • We the bit is not set, we JUMPED
    • MOve content of [rsp + 0x80] 0xfffff880`05b16fb0 which is 0 into @sil
    • Get the next entry in the IRP list ( the list is empty now).
    • Move content of [rsp + 0x80] 0xfffff880`05b16fb0 which is 0 into @rax
    • Release the IrpListLock @ [r15 + 0x480]
    • Move byte at [rsp + 0x98] 0xfffff880`05b16fc8 which is 1
    • Move [rbp + 0x68] which is 0xfffff880`047e6b98 into @rcx
    • Move content of [rsp + 0x88] at addr ss:0018:fffff88005b16fb8=607bf70280faffff into @rbx resulted in (0xfffffa8002f77b60)
    • add 0x40 to rsp, <— adjusting the stack pointer for local variable
    • jump to 0xfffff800`0288b600 (nt!KeSetEvent)
      nt!KeSetSEvent
    • move content @r8b (0) of [rsp + 0x18] @addr fffff880`05b16fc0
    • move content of @edx (0) into dword at [rsp + 0x10]
    • adjust stack pointer to reserver space for local variable.
    • move @rcx 0xfffff880`047e6b98 - address a few bytes after _KAPC object, into @rbx
    • At this point we seems to have exited from the function.

(L1) I don’t understand why the code add 2 into the Irp->StackCount and then tries to compare with Irp->CurrentLocation.
(L2) The address looks like a few bytes following _KAPC structure. I dont know what it is.
I tried to change the value from 0 to 1 but my cancel routine was not called.
(L3) Similar to (L2). Address is a few bytes after _KAPC structure.
I tried to change the value from 0 to 1 but my cancel routine was not called.
(L4) I tried to set the IRP->Flags from (0x40060a00) to (0x40060a0A) but that didn’t help.
(L5) I tried to set the IRP->Flags from ((0x40060a00) to both (0x40060a84) and (0x40060a04) and
my cancel routine is called. My write operation is a synchronous write, but I wonder why the IRP_SYNCHRONOUS_API 0x00000004 is not set.

So I found out a flag that could cause my cancel routine to be called. In addition, my driver should never try to modify the IRP->Flags, so there is some setting that I missed or set, that cause the IO Manager to set such flags (0x40060a00) value when sending the IRP to my driver.
I am at lost as to the reasoning behind this behavior.
Any thought?