Well, THIS one was a surprise…After triggering a memory leak in a driver, the system surprisingly crashed due to a call to FsRtlIsNameInExpression:
# ChildEBP RetAddr
00 b7226d0c 816167b8 nt!RtlpBreakWithStatusInstruction
01 b7226d60 816161c1 nt!KiBugCheckDebugBreak+0x1f
02 b7227154 81575746 nt!KeBugCheck2+0x7b3
03 b7227178 8157567d nt!KiBugCheck2+0xc6
04 b7227198 816126ed nt!KeBugCheckEx+0x19
05 b72271b4 81590522 nt!KiFatalExceptionHandler+0x1a
06 b72271d8 815904f4 nt!ExecuteHandler2+0x26
07 b722729c 8159049b nt!ExecuteHandler+0x24
08 b72275cc 814c61fe nt!RtlRaiseStatus+0x47
09 b7227610 89a72012 nt!RtlIsNameInExpression+0x74
0a b7227630 89a71496 Osr!MatchNameAgainstExpression+0x42
As best we could tell we were passing valid input to FsRtlIsNameInExpression, so what’s up?
The answer lies in the exception code: STATUS_NO_MEMORY (0xC0000017). A quick scan of the disassembly shows that yes indeed, FsRtlIsNameInExpression will raise an exception if:
- The IgnoreCase argument is TRUE
- The UpcaseTable argument is NULL
- RtlUpcaseUnicodeString returns a failure status
It’s possible that there are other cases where it raises an exception, but one case is sufficient to require all calls being wrapped in a __try/__except.
Sadly, we at OSR have used this function for over a decade and never wrapped it in a __try/__except. We even missed it back in 2003 when we tried to document which driver functions raise exceptions. To be fair, the documentation does not indicate that it can raise an exception and the prototype is not properly SAL’d to indicate that an exception is possible.
Hence the PSA: check your calls to FsRtlIsNameInExpression. If you pass TRUE to IgnoreCase and a NULL UpcaseTable you need to wrap your call in a __try/__except to avoid a low memory bugcheck.