天天看点

驱动中为啥少有异常处理的代码?

    今天整理ppt,突然想到一个现象:在WRK以及winddk sample源码中很少见到异常处理相关的影子(指windows结构化异常处理SEH:__try/__exception,而不是C++标准提供的try/catch,kernel不支持C++异常)。按说,内核本身具有完整的异常处理机制,驱动作为内核的一部分,如果使用了异常处理块,必然受到异常处理机制的保护。

    我首先想到的是在DISPATCH_LEVEL上发生了缺页异常,由于换页线程不被调度,因此该异常始终无法解决。在这样的情景下,异常处理的确没有任何意义。但是排除这种情况我实在想不出不使用异常处理的理由。查了一下MSDN "​​Handling Exceptions​​",他的解释是:

1.读写来自用户空间的内存时,需要在try/except块中调用ProbeForWrite/ProbeForRead来检查这部分内存是否可用。形如:

try {
    ...
    ProbeForWrite(Buffer, BufferSize, BufferAlignment);

    /* Note that any access (not just the probe, which must come first,
     * by the way) to Buffer must also be within a try-except.
     */
    ...
} except (EXCEPTION_EXECUTE_HANDLER) {
    /* Error handling code */
    ...
}      

这部分内容我并没有觉得陌生,在Wrk源码中不乏有像NtReadFile/NtWriteFile这样从用户态进入到内核态的stub函数,在其入口处会调用ProbeForxxx函数来检查参数的可用性。

2.驱动中可以调用ExRaiseAccessViolation等函数抛出异常,驱动需要及时处理这些异常。我想,应该没人会冒着蓝屏的风险做这样的操作吧,所以这个可以忽略。

3.部分Mm*系列的DDI,因为访问了用户空间内存需要由try/exception保护。这部分DDI在其Remark部分标注有类似"Calls to MmProbeAndLockPages must be enclosed in a try/except block. If the pages do not support the specified operation, the routine raises the STATUS_ACCESS_VIOLATION or other exceptions."的字样。换句话说,这类DDI是已知的雷区,进入前得有出意外的准备。

观察上面3点,至少可以确定OS对驱动提供了有限的异常处理能力。但是,还是没有让我释疑。倒在另一篇MSDN "​​Exception Handling When Accessing User-Mode Memory​​"中找到一句话:

"Note Aside from accessing and copying the user-mode value into a local variable, 
the driver should not perform any other operations inside the __try block.
 Other operations can cause their own exceptions to occur.
 The operating system handles these exceptions differently."      
"Exceptions processing takes a lot of space in stack 
and this can cause its overflow as stack size in kernel mode is very small (about 12 Kb). 
That's why one should work with exceptions very carefully."      

继续阅读