- 示例工程
【免费下载链接】Windows-driver-samples
This repo contains driver samples prepared for use with Microsoft Visual Studio and the Windows Driver Kit (WDK). It contains both Universal Windows Driver and desktop-only driver samples.
Static Driver Verifier(SDV,静态驱动程序验证器)是随 Windows Driver Kit(WDK)附带的静态验证工具,能够系统性地分析内核模式驱动源码并报告 DDI 使用规则的违反情况。本篇以 SDV-FailDriver-STORPORT 示例为对象,完整讲解如何在 Visual Studio 中对一个 Storport 迷你端口驱动运行 SDV、选择规则集、解读报告,并结合lsi_u3.c源码逐条剖析 7 处被故意注入的缺陷,让你既能跑通 SDV 的完整流程,也能理解每条 DDI 规则在真实代码里到底在检查什么。
示例概览:为 SDV 教学而生的"反面教材"
SDV-FailDriver-STORPORT 是 Windows-driver-samples 仓库中 tools/sdv/samples 目录下的 SDV-FailDriver 系列示例之一,同系列还包括 SDV-FailDriver-KMDF、SDV-FailDriver-NDIS 和 SDV-FailDriver-WDM,分别针对 WDF、NDIS、WDM 与 Storport 四种驱动模型演示 SDV 的检错能力。
该示例的主体是一个模拟 LSI Logic 53C1010(Ultra 3)SCSI 控制器的 Storport 迷你端口驱动,源码中故意埋入了符合 Storport DDI 规则的代码缺陷,用于展示 SDV 如何发现这些错误:
[!CAUTION] 这些示例驱动包含故意制造的代码错误,仅用于展示 SDV 的能力与特性。它们不是可用的功能驱动,也不应作为真实驱动开发项目的参考范例。切勿将其缺陷模式照搬到生产代码中。
从工程文件 driver/lsi_u3.vcxproj 可以看到该驱动以DriverType=Miniport构建,PlatformToolset=WindowsKernelModeDriver10.0,目标平台为 Universal Windows 驱动,提供 x64 与 ARM64 的 Debug/Release 配置,并在链接阶段依赖storport.lib与ntoskrnl.lib——这些正是 Storport 迷你端口驱动的典型工程特征。
源码布局:一个仿真 SCSI 控制器迷你端口驱动
示例由以下文件组成:
| 文件 | 作用 |
|---|---|
| driver/lsi_u3.c | 迷你端口驱动主实现(约 8800 行),内含全部缺陷注入点 |
| driver/lsi_u3.h | 私有头文件:SDV 角色类型、53C1010 SIOP 寄存器映射、设备扩展结构 |
| driver/sdv-user.sdv | SDV 规则清单文件,逐行列出本示例默认验证的 7 条规则 |
| driver/lsi_u3.vcxproj | Visual Studio 工程文件(含 .Filters 筛选器文件) |
| driver/lsi_u3.rc | 资源文件 |
| SDV-FailDriver-STORPORT.sln | 解决方案文件 |
lsi_u3.c模拟了一颗 53C1010 芯片的完整迷你端口行为:定义了HW_DEVICE_EXTENSION(设备扩展,含脚本物理地址入口点、队列指针、NVM 数据等)、HW_NONCACHED_EXTENSION(通过 DMA 访问的非缓存扩展,含 SCSI 脚本数组、协商消息缓冲、队列等)以及SRB_EXTENSION(SRB 扩展,含物理断点计数、协商标志、自动请求感知标志等)。迷你端口通过LsiU3FindAdapter、LsiU3HWInitialize、LsiU3StartIo、LsiU3ISR、LsiU3Reset、LsiU3AdapterControl、LsiU3BuildIo等回调与 Storport 交互。
值得关注的是 driver/lsi_u3.h 中有一段"SDV role types"声明,它把迷你端口回调函数显式地声明为 Storport 的角色类型(如HW_INITIALIZE LsiU3HWInitialize;、HW_STARTIO LsiU3StartIo;、HW_INTERRUPT LsiU3ISR;、HW_FIND_ADAPTER LsiU3FindAdapter;、HW_RESET_BUS LsiU3Reset;、HW_ADAPTER_CONTROL LsiU3AdapterControl;、HW_BUILDIO LsiU3BuildIo;)。这正是 SDV 能够分析 Storport 驱动的关键机制:通过角色类型声明,SDV 才能识别驱动中每个回调的语义约定,并据此应用对应的 DDI 合规规则。文件修订历史还注明 "Aug-2010: Added defect injections for StorPort rules, checked in as fail driver",与示例的定位一致。
运行 SDV 的完整操作步骤(VS + WDK)
SDV 集成在 Visual Studio 的驱动开发工作流中,以下步骤来自示例 README.md,并补充了工程层面的前提说明。
第 1 步:启动 SDV
- 在解决方案资源管理器(Solutions Explorer)中选中驱动工程
lsi_u3.vcxproj。 - 从Driver(驱动程序)菜单中点击Launch Static Driver Verifier...。
- 此时会打开 Static Driver Verifier 应用程序窗口,你可以在这里控制、配置并调度SDV 的分析任务。
启动前请确认工程已配置为 Miniport 驱动类型(如本示例的lsi_u3.vcxproj所示),并已安装匹配目标 Windows 版本的 WDK 工具集。
第 2 步:选择规则集
点击Rules(规则)选项卡,选择在分析启动时需要对驱动校验哪些DDI 使用规则。
SDV 会自动检测当前驱动的类型(WDF、WDM、NDIS 或 Storport),并为该驱动类型预选一组默认规则。如果是第一次对驱动运行 SDV,建议直接使用默认规则集。若想缩短示例驱动的分析耗时,可以选择Custom rule selection(自定义规则选择):
- 使用默认规则集;或
- 选择Custom rule selection,点击Clear All(全清),然后为 Storport 示例勾选以下 7 条规则:
- StorPortAllocatePool2——校验 StorPort 内存分配/释放的配对与返回值检查
- StorPortDeprecated——校验驱动是否调用了已弃用的 Storport 接口
- StorPortEnablePassive——校验被动初始化(passive initialization)注册的调用时机与上下文
- StorPortNotification2——校验 StorPortNotification 通知调用及其结果处理
- StorPortSpinLock——校验自旋锁的获取/释放是否成对、锁类型是否合法
- StorPortStartIo——校验 StartIo 回调路径中的执行约束
- StorPortStatusPending——校验 SRB 状态置为 PENDING 时的完成语义
这份清单与工程中的 driver/sdv-user.sdv 文件逐行一致——该文件正是 SDV 默认加载的规则选择清单。
第 3 步:开始分析
点击Main(主)选项卡,点击Start(开始)按钮。点击 Start 后会弹出提示,告知静态分析已被调度、分析可能耗时较长,点击OK继续即可。
分析期间 SDV 会在 Main 选项卡上持续报告分析状态;分析结束后会给出结果与统计信息。若驱动未能满足某条 DDI 使用规则,结果将以defect(缺陷)形式报告。
7 条 Storport DDI 规则与缺陷注入点逐一对照
lsi_u3.c源码中的每处缺陷都用醒目的Defect injection for: <规则名>.slic注释块标注,方便对照学习。下表汇总了 7 条规则对应的注入位置,随后逐条展开:
| SDV 规则 | 注入点所在函数 | 注入位置(lsi_u3.c 行号) |
|---|---|---|
| StorPortAllocatePool2 | StartNegotiations | 分配段约 L2464,释放段约 L2586 |
| StorPortDeprecated | LsiU3FindAdapter / BusResetPostProcess | 约 L1708 与 L799 |
| StorPortEnablePassive | LsiU3ISR | 约 L2185 |
| StorPortNotification2 | LsiU3FindAdapter | 约 L1694 |
| StorPortSpinLock | SynchronizeReset | 约 L2333 |
| StorPortStartIo | LsiU3StartIo | 约 L2666 |
| StorPortStatusPending | LsiU3StartIo | 约 L2715 |
行号以当前仓库源码为准,不同历史版本可能有细微偏移;建议以
Defect injection for:注释为检索锚点定位。
StorPortAllocatePool2:分配返回值未检查
该规则要求驱动对 StorPort 分配类 API 的返回值进行校验,且分配与释放必须成对、次数匹配。注入点采用"Part A / Part B"两段式缺陷:
- Part A(约 L2459-2464):驱动调用
StorPortAllocatePool(DeviceExtension, 4096, 'SPOO', &bufferPointer)后没有检查 bufferPointer 是否为 NULL; - Part B(约 L2584-2586):随后无条件调用
StorPortFreePool(DeviceExtension, bufferPointer)释放该缓冲区。
源码注释直白地说明了违规本质:"The driver fails the rule because it doesn't check the return value of the allocation and unconditionally attempts to free the buffer."(驱动因未检查分配返回值并无条件尝试释放缓冲区而违反规则。)一旦分配失败,对 NULL/无效指针的释放将造成内存管理错误——这正是 SDV 要拦截的典型缺陷。
StorPortDeprecated:使用已弃用的 Storport API
该规则检查驱动是否调用了已被弃用的 Storport 接口。示例注入了两处:
- Part A(约 L1707-1713):在
LsiU3FindAdapter中调用StorPortValidateRange(...)来校验 I/O 范围,而该接口属于已弃用 API(源码注释注明 "this call was already in the driver"); - Part B(约 L796-804):在
BusResetPostProcess中调用StorPortCompleteRequest(...)完成总线复位后挂起的所有请求。
值得注意的是,BusResetPostProcess上方有一段醒目的 WARNING 注释(约 L783-793),引用 2008 年微软 Windows 驱动开发者大会的建议:不要在迷你端口中使用StorPortCompleteRequest,因为它会导致竞态条件,可能造成请求被双重完成或永远无法完成;推荐做法是遍历迷你端口当前持有的所有请求,逐个用StorPortNotification(RequestComplete)完成。示例特意保留这段注释,用于解释为何StorPortCompleteRequest被视为弃用调用。
StorPortEnablePassive:被动初始化调用位置错误
该规则校验StorPortEnablePassiveInitialization的调用时机。注入点位于LsiU3ISR中断服务例程中(约 L2184-2188):
// Defect injection for: StorPortEnablePassive.slic if (StorPortEnablePassiveInitialization(DeviceExtension, LsiU3PassiveRoutine)) { }在中断上下文(ISR)中注册被动初始化回调本身就违反了该规则对调用环境的约束。与之配套,文件末尾(约 L8850 起)定义了缺陷注入配套的被动初始化例程LsiU3PassiveRoutine,头文件 driver/lsi_u3.h 中也有HW_PASSIVE_INITIALIZE_ROUTINE LsiU3PassiveRoutine;的声明(注释注明 "added as part of a defect injection")。也就是说,这条规则缺陷由"注册调用 + 配套回调"两部分构成,SDV 需要跨函数路径才能完整验证。
StorPortNotification2:通知调用结果未校验
注入点位于LsiU3FindAdapter中(约 L1692-1694):
// Defect injection for: StorPortNotification2.slic StorPortNotification(GetExtendedFunctionTable, DeviceExtension, &pExtFuncTable);驱动向 Storport 请求扩展功能表(Extended Function Table),但未对返回的pExtFuncTable做有效性检查(局部变量初始化为 NULL 后直接使用)。函数前部还声明了PSTORPORT_EXTENDED_FUNCTIONS pExtFuncTable = NULL; // defect injection,表明该变量专为缺陷注入而引入。若扩展功能表获取失败,后续对空指针的解引用将引发严重故障,SDV 通过该规则能够捕捉到这一未校验路径。
StorPortSpinLock:自旋锁未成对获取/释放
注入点位于SynchronizeReset(约 L2331-2333):
// Defect injection for: StorPortSpinLock.slic StorPortAcquireSpinLock(DeviceExtension, InterruptLock, NULL, &IsrLockHandle);驱动在复位同步路径中获取了自旋锁(局部变量STOR_LOCK_HANDLE IsrLockHandle={0}; // defect injection亦为注入所需),但在该路径上没有对应的释放操作。自旋锁获取与释放不配对会破坏 IRQL 状态并可能造成死锁或同步错误,这是 StorPortSpinLock 规则检查的核心语义。
StorPortStartIo:StartIo 路径中的无谓延时
注入点位于LsiU3StartIo的普通 SCSI I/O 路径入口(约 L2664-2666):
// Defect injection for: StorPortStartIo.slic StorPortStallExecution(1000);驱动在 StartIo 主路径中调用StorPortStallExecution(1000)主动忙等延时 1 毫秒。StartIo 回调承担着吞吐量敏感的关键路径职责,不应引入无谓的忙等停顿——SDV 通过该规则识别出这条会降低 I/O 性能并可能影响同步语义的调用。
StorPortStatusPending:PENDING 状态的错误处理
注入点位于LsiU3StartIo中处理 SRB 请求的分支(约 L2712-2715):
// Defect injection for: StorPortStatusPending.slic Srb->SrbStatus = SRB_STATUS_PENDING; StorPortNotification( RequestComplete, DeviceExtension, Srb );驱动把请求状态置为SRB_STATUS_PENDING后立即调用StorPortNotification(RequestComplete)完成该请求。正确语义是:当驱动设置 PENDING 状态时,意味着该请求尚未完成、稍后才完成,此时不应立刻发送完成通知。这种"既标记 PENDING 又马上完成"的矛盾行为正是 StorPortStatusPending 规则要拦截的缺陷——它会让 Storport 认为请求仍处于挂起状态,从而引发状态机混乱。
查看与分析 SDV 结果
静态分析进行时,SDV 会在Main选项卡上报告分析进度;分析完成后报告结果与统计信息。对于本示例,SDV 在默认 7 条规则下会发现 7 处缺陷(defects)。
查看缺陷的流程如下:
- 在Main选项卡的Results(结果)区域点击Rules子选项卡,这里列出上一次运行中每条被验证规则及其分析结果。
- 点击Results(结果)列中的Defect链接,SDV 会打开报告页(Static Driver Verifier Report Page)与跟踪查看器(Trace Viewer),展示从入口到规则违反点的完整代码路径跟踪。
跟踪查看器中的"缺陷路径"是理解每条规则最有价值的部分:它把函数调用链逐步还原,展示缺陷是从哪个回调进入、经过哪些调用、最终在哪一步违反规则。对照本示例源码中Defect injection for:注释标注的位置,你可以在报告中验证 SDV 是否精确命中了每一处注入点——这正是该示例的核心教学价值。
源码中更多可探索的缺陷注入点
除默认 7 条规则外,lsi_u3.c中还标注了若干其他规则的缺陷注入点,它们不在 driver/sdv-user.sdv 的默认清单中,但可作为进阶练习在Custom rule selection中自行添加对应规则验证:
| 注入点标注的规则 | 大致位置 | 缺陷模式 |
|---|---|---|
| StorPortAdapterControl | 约 L1623-1625 | LsiU3AdapterControl一律返回ScsiAdapterControlUnsuccessful |
| StorPortVirtualDevice | 约 L1733-1734 | ConfigInfo->VirtualDevice = TRUE强制声明虚拟设备 |
| StorPortFindAdapter | 约 L1909-1911 | 注释掉MaximumTransferLength赋值,使其保持未初始化值 |
| StorPortMSILock | 约 L1931-1933 与 L2634-2637 | 声明使用 MSI 中断同步模式,却在非 MSI 驱动中获取 MSI 锁 |
| StorPortPerfOpts | 约 L2091-2093 | StorPortInitializePerfOpts调用方式不当(注释标注 SDV Result: TIMEOUT/SPACEOUT) |
| StorPortIrql | 约 L2141-2143 | 在 ISR 中调用StorPortGetGroupAffinity |
| StorPortAllocatePool | 约 L2812-2813 | 未知 SRB 函数分支中的分配未配对释放 |
| StorPortNotification | 约 L3099-3102 与 L3357-3358 | 请求完成通知后再访问ActiveRequest、完成后才调用StorPortGetOriginalMdl("完成后使用"型缺陷) |
这些注入点同样以注释块标注,适合作为扩展练习验证 SDV 在不同规则集下的检错覆盖面。
总结与使用注意
- 运行闭环:在 Visual Studio 中通过Driver → Launch Static Driver Verifier...启动 SDV → 在Rules选项卡选择规则集(默认或自定义)→ 在Main选项卡点击Start调度分析 → 通过Results → Defect链接进入报告页与 Trace Viewer 查看缺陷路径。
- 7 条规则、7 处缺陷:默认规则集下 SDV 对本示例报告 7 处缺陷,覆盖内存分配检查、弃用 API、被动初始化时机、通知结果校验、自旋锁配对、StartIo 延时与 PENDING 状态语义七类典型问题。
- 源码对照学习:所有缺陷均在 driver/lsi_u3.c 中以
Defect injection for: <规则名>.slic注释标注,规则清单固化在 driver/sdv-user.sdv,角色类型声明见 driver/lsi_u3.h。 - 务必牢记:本示例驱动的缺陷是故意注入的,驱动本身不可运行、不可部署。它的正确用途是作为 SDV 的教学标本:先在真实驱动上跑通 SDV 流程,再回到这份"错题集"里逐条印证规则含义,从而真正理解每条 DDI 规则在检查什么。
- 示例工程
【免费下载链接】Windows-driver-samples
This repo contains driver samples prepared for use with Microsoft Visual Studio and the Windows Driver Kit (WDK). It contains both Universal Windows Driver and desktop-only driver samples.
相关推荐
StorAhci StorPort AHCI 迷你端口驱动深入解析:从 DriverEntry 到 AHCI 控制器编程
StorAhci StorPort AHCI 迷你端口驱动深入解析:从 DriverEntry 到 AHCI 控制器编程 本指南以 Windows driver
示例工程Windows-driver-samples 实战:用 DV-FailDriver-WDM 验证 Driver Verifier 与 Device Fundamentals 测试如何捕获注入缺陷
Windows driver samples 实战:用 DV FailDriver WDM 验证 Driver Verifier 与 Device Fundam
示例工程音乐解锁终极指南:简单三步让加密音乐重获自由
音乐解锁终极指南:简单三步让加密音乐重获自由 你是否曾经为下载的音乐只能在特定播放器中打开而烦恼?QQ音乐、网易云音乐、酷狗音乐等平台的加密格式让你无法自由享受
示例工程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考