创建可靠的内核模式驱动程序
驱动程序在内核模式下执行的总代码中占很大比例。 内核模式驱动程序实际上是操作系统的组件。 因此,可靠且安全的驱动程序对操作系统的整体可信度做出了重大贡献。 若要创建可靠的内核模式驱动程序,请遵循以下准则:
正确保护设备对象。
用户对系统驱动程序和设备的访问权限由系统分配给设备对象的安全描述符控制。 大多数情况下,系统在安装设备时设置设备安全参数。 有关详细信息,请参阅 创建安全设备安装。 有时,驱动程序在控制对其设备的访问方面发挥作用是合适的。 有关详细信息,请参阅 保护设备对象。
正确验证设备对象。
如果驱动程序创建多种类型的设备对象,则必须检查它在每个 IRP 中接收的类型。 有关详细信息,请参阅 验证设备对象的失败。
使用“安全字符串”函数。
操作字符串时,驱动程序应使用安全字符串函数,而不是 C/C++ 语言运行时库提供的字符串函数。 有关详细信息,请参阅 使用安全字符串函数。
验证对象句柄。
接收对象句柄作为输入的驱动程序必须验证句柄是否有效、可访问以及是否属于预期类型。 有关使用对象句柄的详细信息,请参阅以下主题:
正确支持多处理器。
切勿假设驱动程序仅在单处理器系统上运行。 有关可用于确保驱动程序在多处理器系统上正常运行的编程技术的信息,请参阅以下主题:
正确处理驱动程序状态。
请务必始终验证驱动程序是否处于假定其状态。 例如,如果驱动程序收到 IRP,它是否已为同一类型的 IRP 提供服务? 如果驱动程序没有针对这种情况检查,则第一个 IRP 可能会丢失。 有关详细信息,请参阅 无法检查驱动程序的状态。
验证 IRP 输入值。
从可靠性和安全角度而言,验证与 IRP 关联的所有值(例如缓冲区地址和长度)至关重要。 以下主题提供有关验证 IRP 输入值的信息:
正确处理 I/O 堆栈。
在驱动程序堆栈向下传递 IRP 时,驱动程序必须调用 IoSkipCurrentIrpStackLocation 或 IoCopyCurrentIrpStackLocationToNext 来设置下一个驱动程序的 I/O 堆栈位置。 请勿编写直接将一个 I/O 堆栈位置复制到下一个 I/O 堆栈位置的代码。
正确处理 IRP 完成操作。
驱动程序不得完成状态值为 STATUS_SUCCESS 的 IRP,除非它实际支持并处理 IRP。 有关处理 IRP 完成操作的正确方法的信息,请参阅 完成 IRP。
正确处理 IRP 取消操作。
取消操作可能难以正确编码,因为它们通常以异步方式执行。 处理取消操作的代码中的问题可能会长时间被忽略,因为此代码通常不会在正在运行的系统中频繁执行。
请务必阅读并了解“ 取消 IRP”下提供的所有信息。 请特别注意 同步 IRP 取消 以及 取消 IRP 时要考虑的要点。
避免与取消操作关联的同步问题的一种方法是实现 取消安全 IRP 队列。 取消安全的 IRP 队列是针对 Windows XP 和更高操作系统版本引入的驱动程序托管队列,但也与早期版本向后兼容。
正确处理 IRP 清理和关闭操作。
请确保了解 IRP_MJ_CLEANUP 和 IRP_MJ_CLOSE 请求之间的区别。 清理请求在应用程序关闭文件对象上的所有句柄之后,但有时在所有 I/O 请求完成之前到达。 关闭请求在文件对象的所有 I/O 请求都已完成或取消后到达。 有关详情,请参阅以下主题:
有关正确处理 IRP 的详细信息,请参阅 处理 IRP 的其他错误。
使用驱动程序验证程序
驱动程序验证程序 是可用于确保驱动程序可靠性的最重要工具。 驱动程序验证程序可以检查各种常见驱动程序问题,包括本部分中讨论的一些问题。 但是,使用驱动程序验证程序并不能取代谨慎、深思熟虑的软件设计。