使用静态驱动程序验证程序查找 Windows 驱动程序中的缺陷

静态驱动程序验证程序(SDV)使用一组接口规则和操作系统模型来确定驱动程序是否与 Windows 操作系统正确交互。 SDV 在驱动程序代码中发现缺陷,这些缺陷可能指向驱动程序中的潜在 bug。

SDV 可以分析符合以下驱动程序模型之一的内核模式驱动程序:WDM、KMDF、NDIS 或 Storport。 有关详细信息,请参阅 支持的驱动程序确定静态驱动程序验证程序是否支持驱动程序或库。 此外,对于不遵循上述驱动程序模型的驱动程序,SDV 提供有限的支持(一个严重限制的规则集,侧重于常规错误,例如 null 取消引用)。

重要

不再支持 SDV,并且 Windows 24H2 WDK 或 EWDK 版本中不提供 SDV。 它在版本 26017 之前的 WDK 中不可用,并且不包括在 Windows 24H2 RTM WDK 中。 通过从 下载 Windows 驱动程序工具包(WDK)下载 Windows 11 版本 22H2 EWDK(2023 年 10 月 24 日发布)和 Visual Studio 生成工具 17.1.5,仍可使用 SDV。 建议仅使用企业 WDK 运行 SDV。 不建议将旧版标准 WDK 与 Visual Studio 的最新版本结合使用,因为这可能会导致分析失败。
今后,CodeQL 将成为驱动程序的主要静态分析工具。 CodeQL 提供了一种功能强大的查询语言,可将代码视为要查询的数据库,使编写特定行为、模式等查询变得简单。 有关使用 CodeQL 的详细信息,请参阅 CodeQL 和静态工具徽标测试

准备源代码

使用以下步骤准备代码进行分析。

  1. 使用函数角色类型声明驱动程序提供的函数

    SDV 要求使用函数角色类型声明声明函数。 例如, 必须使用DRIVER_INITIALIZE函数角色类型声明 DriverEntry 例程:

    DRIVER_INITIALIZE DriverEntry;
    

    声明后,将实现回调例程(或定义),如下所示:

    /
    // Driver initialization routine
    //
    NTSTATUS
      DriverEntry(
        _In_ struct _DRIVER_OBJECT  *DriverObject,
        _In_ PUNICODE_STRING  RegistryPath
        )
      {
          // Function body
      }
    

    每个支持的驱动程序模型都有一组用于驱动程序回调函数和调度例程的函数角色类型。 这些函数角色类型在 WDK 头文件中声明。 例如,下面是在 Wdm.h 中显示的DRIVER_INITIALIZE角色类型的函数原型。

    /
    // Define driver initialization routine type.
    //
    _Function_class_(DRIVER_INITIALIZE)
    _IRQL_requires_same_
    typedef
    NTSTATUS
    DRIVER_INITIALIZE (
        _In_ struct _DRIVER_OBJECT *DriverObject,
        _In_ PUNICODE_STRING RegistryPath
        );
    
    typedef DRIVER_INITIALIZE *PDRIVER_INITIALIZE;
    

    由于函数角色类型已在 WDK 头文件中定义,因此只需将回调函数声明为该类型。 在本例中,将 DriverEntry 声明为类型DRIVER_INITIALIZE。 有关驱动程序模型函数角色类型的完整列表,请参阅 “使用函数角色类型声明”。

  2. 运行 C/C++的代码分析

    为了帮助你确定源代码是否已准备好,请在 Visual Studio 中运行 代码分析工具 。 代码分析工具检查 SDV 所需的函数角色类型声明。 当函数定义的参数与函数角色类型中的参数不匹配时,代码分析工具可以帮助识别可能错过或发出警告的任何函数声明。

    • 在 Visual Studio 中打开驱动程序项目。
    • “生成 ”菜单中,单击“ 解决方案上的运行代码分析”。

    结果将显示在 “代码分析 ”窗口中。 修复你可能错过的任何函数声明。 还可以配置代码分析工具,以便在生成解决方案时运行它。

    下表显示了代码分析工具可能会在驱动程序代码中找到的一些警告。 若要运行静态驱动程序验证程序,驱动程序需要摆脱这些缺陷。

    C/C++ 警告的代码分析 说明
    C28101 驱动程序模块已推断出当前函数是 <function-type> 函数
    C28022 此函数上的函数类与用于定义它的 typedef 的函数类不匹配。
    C28023 要分配或传递的函数应具有针对至少一个类的 _Function_class_ 批注
    C28024 要分配到的函数指针是用函数类批注的,它未包含在函数类列表中。
    C28169 dispatch 函数 <函数> 没有任何_Dispatch_type_注释
    C28208 函数签名与函数声明不匹配

运行静态驱动程序验证程序

  1. 在 Visual Studio 中打开驱动程序项目 (.vcxProj) 文件。 从驱动程序 菜单,单击启动静态驱动程序验证程序...

    这会打开静态驱动程序验证程序应用程序,可在其中控制、配置和计划静态驱动程序验证程序执行分析的时间。

  2. 如果驱动程序包含库,请单击“ ”选项卡,然后单击“ 添加库”以添加库

    浏览到库源代码的目录,然后选择项目文件(.vcxProj)。 添加驱动程序包括的所有库。 必须先添加库,然后 SDV 才能分析驱动程序。 开始分析驱动程序时,SDV 还会分析尚未处理的库。 处理库后,它将存储在全局 SDV 缓存中。 有关详细信息,请参阅 静态驱动程序验证程序中的库处理

  3. 检查静态驱动程序验证程序的配置设置。 单击“配置” 选项卡。

    项目配置项目配置 显示你在 Visual Studio 中选择的配置和平台设置。

    在大多数情况下,可以使用默认设置。 如果 SDV 报告 Timeout、GiveUp 或 Spaceout,可以尝试调整这些设置。 有关详细信息,请参阅 有关对静态驱动程序验证程序进行故障排除的建议。

    计划 选择开始验证的开始时间。 默认设置是在单击“主”选项卡上的“开始后立即开始分析。根据驱动程序的大小及其复杂性,静态分析可能需要很长时间才能运行。 你可能希望计划分析在最方便的时候开始;例如,在一天结束时。

    注意

    请务必检查计算机的电源管理计划,以确保计算机在分析期间不会进入睡眠状态。

  4. 单击“规则”选项卡,选择要在启动分析时验证的驱动程序 API 使用规则。

    静态驱动程序验证程序会检测要分析的驱动程序类型(WDF、WDM、NDIS 或 Storport),并选择驱动程序类型的默认规则集。 如果这是你第一次在驱动程序上运行 SDV,则应运行默认规则集。

    有关规则的信息,请参阅 DDI 符合性规则

  5. 启动静态分析。 单击“主”选项卡,然后单击“开始”。 单击“开始时,将显示一条消息,告知你已计划静态分析,并且分析可能需要很长时间才能运行。 单击 “确定” 继续。 分析从计划开始。

查看和分析结果

随着静态分析的进行,SDV 报告分析的状态。 分析完成后,SDV 会报告结果和统计信息。 如果驱动程序无法满足 API 使用规则,结果将报告为缺陷。

如果遇到任何问题,SDV 会在 “警告 ”和 “错误 ”页上显示这些问题。 “ 驱动程序属性” 页显示某些驱动程序属性的测试结果。 驱动程序属性测试用于标识驱动程序功能以进一步限定分析。 可以使用 驱动程序属性 结果来确认驱动程序的预期属性和支持的功能。

若要查看静态驱动程序验证程序报告中的特定缺陷,请单击“结果”窗格中的缺陷”。 这会打开 跟踪查看器,该查看器显示规则冲突的代码路径的跟踪。 有关详细信息,请参阅 解释静态驱动程序验证程序结果

注意

静态驱动程序验证程序保留分析的结果和设置。 若要清除结果,请单击“ 清理”。

静态驱动程序验证程序结果疑难解答

如果 SDV 报告未找到任何缺陷,请检查 “主 ”选项卡以确保检测到入口点。 如果驱动程序未使用函数角色类型声明函数,SDV 将无法分析和查找驱动程序代码中的缺陷。 有关详细信息,请参阅 “使用函数角色类型声明”。

如果 SDV 报告超时或无法返回有用的结果,则可能需要更改几个 SDV 配置选项。 有关如何对 SDV 进行故障排除的详细信息,请参阅 有关对静态驱动程序验证程序进行故障排除的建议。

确定静态驱动程序验证程序是否支持驱动程序或库

使用函数角色类型声明

静态驱动程序验证程序规则

代码分析工具