6.2 有效性判断
在内核编程过程中,肯定需要向系统申请某些资源,常见的资源有内存、文件、事件对象、线程对象等。在理想的情况下,这些资源申请均可成功,但是在系统资源不足的情况下,资源申请可能会失败。
最常见的例子是内存申请失败,请看下面例子:
在绝大多数情况下,上面的申请操作不会失败,但是一旦失败,由于缺乏错误处理,memset函数访问空指针会直接导致蓝屏。
再看一个例子:
上面的代码很简单,在内核驱动的入口函数中,设置一个Create的派遣函数,上述代码在99.9%的情况下工作良好,但是笔者却遇到过这段代码蓝屏!后来通过调试发现,这个驱动是被一个第三方驱动加载的,并非被系统加载,而这个第三方驱动模拟了系统加载驱动的行为,通过这种方式加载的驱动,没有实体的驱动对象对应,所以DriverEntry函数中的DriverObject为NULL。上述代码没有对DriverObject进行NULL判断就直接使用,触发了一个空指针异常。
通过这个极端的例子,笔者想传递一个理念给读者:在内核态中,所有资源都必须被认为是“不可信”的,开发者在使用一个外部资源时,必须对其进行有效性的判断。
对于上面这个例子,读者可能比较好奇,为什么需要一个第三方驱动加载另外一个驱动?在实际的项目中,从Windows 10 14393版本开始,驱动需要打EV(Extended Validation)签名,为了减少打EV签名所带来的时间或金钱成本,有些公司会开发一套驱动,这个驱动被打上EV签名,该驱动本身没有任何业务逻辑,只负责加载其他驱动,而这些其他驱动,本身没有任何签名。这样的好处是:对于小公司来说,永远只需要打一次EV签名,其他驱动都通过这个EV签名驱动加载。
请读者谨记一点,在内核中,无论是访问自身申请的资源,还是访问外部传递进来的资源,都必须进行有效性判断。