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.