When and how KeExpandKernelStackAndCalloutEx is expected to be used?

I’m aware that the kernel stack is very limited - Are there any guidelines for when I should expand the stack? Can you give me some examples of real use cases?
I guess that in most cases it’s pretty hard to know when you should expand the stack… well, I guess that the guideline is something like: In paths that you think the stack space can be large, call IoGetRemainingStackSize() to check the bytes left and call KeExpandKernelStackAndCalloutEx if necessary… The question is: In which cases do you think this check is necessary?
Also, when I do expand the stack, can I reference memory from the previous stack segment or is it not guaranteed to be valid?

Thanks!

We usually only use this if we are calling outside our own driver. That’s really the only case where you have no idea or control over how much stack will be used. For example, in Windows the file systems use this API before calling down to the lower layers of the storage branch.

The previous stack segment will remain valid for the duration of this call.

Yeah but you tell me every time you call ZwOpenProcess you do it through KeExpandStackCallout? ZwOpenProcess invokes callbacks of other drivers that may use stack space…

If you were in an IOCTL handler for a control device and called ZwOpenProcess I’d be surprised if it overflowed the stack. It could happen, but it’s mitigated by a) the stack is fairly fresh when you’re called and b) the fact that the Ps callbacks are call out and not call through. Compare this to the I/O subsystem and the number of stack frames you can end up with before getting to the end of the line. From the storage branch of a test VM right now:

1: kd> kc

Call Site

00 storport!RaUnitScsiIrp
01 storport!RaDriverScsiIrp
02 nt!IopfCallDriver
03 nt!IovCallDriver
04 nt!IofCallDriver
05 CLASSPNP!SubmitTransferPacket
06 CLASSPNP!ServiceTransferRequest
07 CLASSPNP!ClassReadWrite
08 CLASSPNP!ClassGlobalDispatch
09 nt!IopfCallDriver
0a nt!IovCallDriver
0b nt!IofCallDriver
0c partmgr!PmWrite
0d partmgr!PmGlobalDispatch
0e nt!IopfCallDriver
0f nt!IovCallDriver
10 nt!IofCallDriver
11 partmgr!PartitionIo
12 partmgr!PartitionWrite
13 partmgr!PmGlobalDispatch
14 nt!IopfCallDriver
15 nt!IovCallDriver
16 nt!IofCallDriver
17 volmgr!VmReadWrite
18 nt!IopfCallDriver
19 nt!IovCallDriver
1a nt!IofCallDriver
1b volume!VolumePassThrough
1c nt!IopfCallDriver
1d nt!IovCallDriver
1e nt!IofCallDriver
1f volsnap!VolsnapWriteFilter
20 volsnap!VolSnapWrite
21 nt!IopfCallDriver
22 nt!IovCallDriver
23 nt!IofCallDriver
24 nt!ViFilterDispatchGeneric
25 nt!IopfCallDriver
26 nt!IovCallDriver
27 nt!IofCallDriver
28 Wdf01000!FxIoTarget::Send
29 Wdf01000!imp_WdfRequestSend
2a OsrFlt!WdfRequestSend
2b OsrFlt!OsrFltUtilSendAndForgetRequest
2c OsrFlt!OsrFltVolumeEvtIoWrite
2d Wdf01000!FxIoQueueIoRead::Invoke
2e Wdf01000!FxIoQueue::DispatchRequestToDriver
2f Wdf01000!FxIoQueue::DispatchEvents
30 Wdf01000!FxIoQueue::QueueRequest
31 Wdf01000!FxPkgIo::DispatchStep2
32 Wdf01000!FxPkgIo::DispatchStep1
33 Wdf01000!FxPkgIo::Dispatch
34 Wdf01000!DispatchWorker
35 Wdf01000!FxDevice::Dispatch
36 Wdf01000!FxDevice::DispatchWithLock
37 nt!IopfCallDriver
38 nt!IovCallDriver
39 nt!IofCallDriver
3a nt!ViFilterDispatchGeneric
3b nt!IopfCallDriver
3c nt!IovCallDriver
3d nt!IofCallDriver
3e Ntfs!NtfsStorageDriverCallout
3f nt!KxSwitchKernelStackCallout
40 nt!KiSwitchKernelStackContinue
41 nt!KiExpandKernelStackAndCalloutOnStackSegment
42 nt!KiExpandKernelStackAndCalloutSwitchStack
43 nt!KeExpandKernelStackAndCalloutInternal
44 nt!KeExpandKernelStackAndCalloutEx
45 Ntfs!NtfsMultipleAsync
46 Ntfs!NtfsNonCachedIo
47 Ntfs!NtfsCommonWrite
48 Ntfs!NtfsFsdWrite
49 nt!IopfCallDriver
4a nt!IovCallDriver
4b nt!IofCallDriver
4c FLTMGR!FltpLegacyProcessingAfterPreCallbacksCompleted
4d FLTMGR!FltpDispatch
4e nt!IopfCallDriver
4f nt!IovCallDriver
50 nt!IofCallDriver
51 nt!IopSynchronousServiceTail
52 nt!NtWriteFile

If you were going to call ZwOpenProcess from within a filter here you might need the expand call because you have no idea how much stack is left.

Usually you find out where you need this API by getting a crash (hopefully in testing but maybe at a customer site). In recent memory we’ve needed it around FltCreateFile calls (they can cause I/O) and a USB filter that sometimes generated I/O as a result of I/O.

I see, thanks for answering.