[论文阅读] 软件工程 | GPS算法:用“路径摘要”当向导,软件模型检测从此告别“瞎找bug”
GPS算法:用“路径摘要”当向导,软件模型检测从此告别“瞎找bug”
论文信息
项目 | 详情 |
---|---|
论文原标题 | Software Model Checking via Summary-Guided Search (Extended Version) |
主要作者及机构 | 1. Ruijie Fang(University of Texas at Austin, USA) 2. Zachary Kincaid(Princeton University, USA) 3. Thomas Reps(University of Wisconsin-Madison, USA) |
APA引文格式 | Fang, R., Kincaid, Z., & Reps, T. (2025). Software Model Checking via Summary-Guided Search (Extended Version). ACM Transactions on Programming Languages and Systems, 1(1), 1-36. https://doi.org/10.1145/nnnnnnn.nnnnnnn |
核心领域 | 软件模型检测、静态分析、自动化测试 |
开源资源 | - Docker镜像:https://hub.docker.com/r/ruijiefang/gps-oopsla25-ae/ - 基准与代码:https://doi.org/10.5281/zenodo.16914158 |
一段话总结
本文提出一种名为GPS(Guided by Path Summaries) 的软件模型检测算法,将程序模型检测转化为“有向导的状态搜索”:通过静态分析生成的“路径摘要”(过近似输入输出关系的公式),既剪枝掉不可行路径、减少无效搜索,又引导测试生成以探索未覆盖状态;其创新的“两层搜索策略”(深度优先执行测试+广度优先生成测试)能高效处理“长输入依赖错误路径”(如需循环1000次的bug),还通过“gas变量 instrumentation”保证了“反驳完备性”(只要有bug,给足够时间就一定能找到)。在SV-COMP(ReachSafety-Loops类别)及“锁钥难题”(Lock & Key)等基准测试中,GPS在解决数量和运行时间上均超越现有顶尖模型检测工具。
研究背景:软件模型检测的“三难困境”与“痛点案例”
要理解GPS的价值,先得搞清楚它解决了什么“老大难”问题。软件模型检测的核心目标是证明程序安全(无bug)或找到bug(反例输入),但现有技术长期陷入“三难困境”:
1. 现有三大技术的“先天缺陷”
技术类型 | 优点 | 致命缺点 | 类比场景 |
---|---|---|---|
静态分析 | 能证明程序安全 | 不能找bug(过近似导致无法反驳) | 医院“全面体检”:能查慢性病,但漏诊急性病 |
自动化测试 | 能找bug(生成测试用例) | 不能证明安全(覆盖不全) | 警察“巡逻抓小偷”:能抓现行,但不能保证无贼 |
传统模型检测 | 既能证安全又能找bug | 慢、易超时、可能不终止(搜索空间爆炸) | “地毯式搜索”:能找全,但费时间且可能迷路 |
2. 两大典型痛点案例(论文重点解决)
痛点1:“锁钥难题”(Lock & Key)——找不到“开门的钥匙”
比如论文中的EX-1程序(图1):要触发断言错误,需满足两个条件:① 循环执行至少1000次;② 输入N=1000
(“钥匙”)。现有工具的问题:
- 测试工具(如DART):随机生成
N
,很难碰巧选中1000,且循环1000次会超时; - 传统模型检测(如BMC):按路径长度递增搜索,没到1000次就耗尽资源。
痛点2:“长路径陷阱”——绕不出“无限循环”
比如论文中的状态机程序(图9):要触发错误,需依次输入“D→O→G”(状态机切换)+“C→A→T”(输入变量)。现有工具可能陷入“无限循环”:每次选c=0
(非关键输入),永远到不了错误状态,导致Explore子程卡死。
3. 核心需求:需要一种“又准又快又全”的方法
- 准:能精准定位“有效路径”,不做无效搜索;
- 快:能跳过冗余路径,快速找到长依赖bug;
- 全:只要有bug,就一定能找到(反驳完备),同时能证明安全。
创新点:GPS的“四大独门绝技”
GPS的突破在于把静态分析的“精准导航”、测试的“高效探索”、模型检测的“完备性”捏合成一个整体,核心创新有四个:
1. 摘要引导测试:不“瞎试”,只试“可能有bug的路径”
传统测试是“广撒网”,GPS是“精准钓鱼”——用静态分析生成的“路径摘要”(过近似输入输出公式)指导测试生成:
- 摘要
Sum(G,u,v)
:代表从节点u
到v
的所有路径的“输入输出关系”(比如“min'=i'=k
且k≥1000
”); - 测试生成逻辑:若
w(π)∘Sum(G,dst(π),t)
(路径π
+从π
终点到错误的摘要)可满足,则从模型中提取“可能触发bug的测试用例”,避免随机试错。
比如EX-1中,摘要直接给出“k≥1000
”,GPS直接生成N=1000
的测试,一次就命中bug。
2. 死端插值:生成“一劳永逸”的不变量
当测试生成失败(测试到不了错误),GPS不是直接放弃,而是提取“死端插值”:
- 死端:路径
π
的所有延伸都到不了错误(比如EX-2中“r≥3
”的路径,不可能满足“r=2
”的错误条件); - 插值:从“
w(π)∘Sum(...)
不可满足”的证明中提取的状态公式(如“2|x
”,表示x
是偶数),能证明“所有延伸路径都不可行”。
比如EX-3中,死端插值“2|x
”直接证明“x
永远是偶数,不可能等于1”,一次性覆盖所有相关路径,不用逐个验证。
3. 两层搜索策略:兼顾“深度探索”与“广度覆盖”
GPS用“广度优先生成测试+深度优先执行测试”的组合策略:
- 广度优先:从路径树的“前沿”(未探索路径)中选路径生成测试,避免陷入某一条无效路径;
- 深度优先:执行测试时深入探索路径,快速覆盖长路径。
这种策略既不会像传统深度优先那样“一条路走到黑”,也不会像纯广度优先那样“浅尝辄止”,特别适合长输入依赖路径。
4. gas instrumentation:给“无限循环”装“刹车”
为了保证“反驳完备性”(只要有bug就一定能找到),GPS给程序加了个gas变量(类似“倒计时”):
- 初始化:
gas
为非确定正整数; - 循环处:每次循环
gas--
,且gas≥0
(否则退出循环); - 作用:强制Explore子程不会陷入“无限循环”,保证所有可能路径都会被公平探索。
比如状态机程序中,gas变量会迫使测试生成“让状态机前进”的输入(如c=68
),否则gas
耗尽就退出,避免卡死。
研究方法:GPS算法的“三步拆解”
GPS的逻辑链条非常清晰,可拆成“基础版(GPSLite)→完整版(GPS)→完备性保障(gas)”三步:
步骤1:GPSLite——核心逻辑的“简化版”
GPSLite是GPS的“骨架”,专注于“找bug+死端检测”,流程如下:
- 初始化:路径树
T
(存已探索路径)、前沿队列frontierQueue
(存待探索路径),初始只有空路径; - 循环探索:
- 从队列取路径
π
,用Check+
原语判断“w(π)∘Sum(G,dst(π),t)
是否可满足”; - 若可满足(SAT):调用
Explore
子程,从模型中提取测试用例,模拟执行并扩展路径树(若到错误则返回bug); - 若不可满足(UNSAT):标记
π
为死端,跳过(剪枝);
- 从队列取路径
- 终止:队列空则证明安全,找到bug则返回反例。
步骤2:GPS——加入“不变量合成”的“完整版”
GPS在GPSLite基础上增加“证明安全”的能力,核心是抽象可达树(ART):
- ART结构:路径树
T
+标签L
(路径的状态公式,如“2|x
”)+覆盖边Cov
(标记归纳不变量); - 死端插值生成:当
Check
原语返回UNSAT时,提取插值序列(如⊤→2|x→2|x
),用Refine
子程强化路径标签,标记死端; - 归纳不变量检查:用
TryCover
子程检查“子路径标签是否蕴含祖先标签”(如“x>1∧2|x
蕴含2|x
”),若满足则加覆盖边,证明循环不变量; - 终止:ART无“前沿路径”(所有路径要么死端要么被覆盖)则证明安全。
步骤3:gas instrumentation——保障“反驳完备性”
为了避免Explore
子程陷入无限循环,GPS对程序做“微小改造”:
- 新增全局变量
gas
,初始为非确定正整数; - 在每个循环头部加代码:
if (gas-- < 0) break;
; - 效果:任何测试用例的执行步数都被
gas
限制,Explore
必然终止,且GPS会“公平探索”所有可能路径(只要有bug,总有一个gas
值能覆盖错误路径)。
关键优化:离线预计算摘要
GPS频繁需要“从任意节点到错误节点的摘要”,为了不重复计算,论文用Tarjan算法对偶化:
- 反转程序控制流图(
G→G^R
); - 定义“反向组合运算符”(
F∘^R G = G∘F
); - 用Tarjan算法一次计算所有“单目标摘要”(从任意节点到错误节点),后续查询直接调用,效率提升线性倍。
主要成果:数据说话,GPS到底有多强?
论文用297个基准程序(SV-COMP+锁钥难题+安全例子)验证GPS,核心结果如下:
1. 三大研究问题(RQ)与结论
研究问题(RQ) | 实验设计 | 核心结论 |
---|---|---|
RQ1:GPS哪些组件最关键? | 消融实验:GPS vs GPSLite(无不变量)vs GPS-nogas(无gas)vs GPS-nogas-nosum(无摘要) | 1. 摘要(sum)是核心:无摘要时锁钥难题仅解决2/57; 2. gas提升稳定性:无gas时状态机例子仅解决3/6; 3. 不变量(GPS vs GPSLite):安全例子从4/9提升到9/9 |
RQ2:GPS vs 基础技术(CRA/Impact) | 对比CRA(静态分析)、Impact(模型检测) | 1. 比Impact多解决142个基准(136→278); 2. 比CRA多证明29个安全程序(156→185),且新增找bug能力 |
RQ3:GPS vs 顶尖工具? | 对比SV-COMP 2024顶尖工具(VeriAbs/Symbiotic/CPAChecker) | 1. 总解决数:GPS 278 > VeriAbs 236 > Symbiotic 183; 2. 锁钥难题:GPS 57/57全解决,VeriAbs仅28/57; 3. 运行时间:GPS平均9675s,比Symbiotic(87358s)快8倍 |
2. 核心贡献总结
贡献类型 | 具体价值 |
---|---|
理论贡献 | 1. 提出“摘要引导搜索”框架,结合静态分析与测试; 2. 证明GPS的“反驳完备性”; 3. 死端插值:首次用摘要生成覆盖无限路径的不变量 |
工程贡献 | 1. Tarjan算法对偶化:离线预计算摘要,提升效率; 2. gas instrumentation:低成本实现完备性 |
实践贡献 | 1. 解决SV-COMP中多数“长路径+输入依赖”难题; 2. 开源工具与基准,供后续研究使用 |
关键问题
Q1:GPS如何解决“锁钥难题”中“找不到关键输入”的问题?
A:靠“摘要引导测试生成”。静态分析生成的路径摘要会直接给出“触发bug的输入约束”(如EX-1中“k≥1000
”),GPS用SMT求解器从摘要中提取“关键输入”(N=1000
),不用随机试错,一次就能命中。
Q2:“死端插值”和传统不变量有什么区别?
A:传统不变量(如Impact算法)是“逐路径生成”,只能覆盖单条路径;死端插值是“基于摘要生成”,能覆盖所有延伸路径(比如“2|x
”能证明所有“x
为偶数”的路径都到不了“x=1
”的错误),质量更高,减少重复计算。
Q3:为什么“gas instrumentation”能保证“反驳完备性”?
A:因为它强制“所有测试用例都有长度上限”(由gas
决定),Explore
子程不会陷入无限循环。GPS按“广度优先”处理前沿路径,只要有bug,总有一个gas
值能覆盖错误路径的长度,且GPS会公平探索所有可能路径,最终一定能找到bug。
Q4:GPS相比传统模型检测(如Impact),为什么更快?
A:有两个关键原因:① 摘要剪枝:提前排除不可行路径,减少搜索空间;② 离线摘要:用Tarjan算法对偶化预计算所有“单目标摘要”,后续查询不用重复分析循环,节省时间;③ 两层搜索:深度优先执行测试+广度优先生成测试,平衡“深度”与“广度”,避免无效探索。
总结:GPS的“得与失”与未来方向
1. 核心优势
- 高效:摘要引导+剪枝,解决长路径、输入依赖bug的效率远超现有工具;
- 全能:既能证明程序安全(死端插值+ART),又能找bug(测试生成);
- 可靠:gas instrumentation保证“反驳完备性”,只要有bug就一定能找到;
- 易用:开源工具+基准,可直接复用,支持线性整数算术(LIA)程序。
2. 现有局限
- 适用范围:目前仅支持“过程内程序”(无函数调用)和“线性整数算术”(无指针、数组);
- 摘要精度:摘要的过近似可能导致“假阳性测试”(生成的测试到不了错误),需额外处理;
- 性能开销:不变量合成(死端插值+TryCover)会增加少量计算成本。
3. 未来方向
- 扩展到“跨过程程序”:结合Spacer等跨过程模型检测框架;
- 支持更复杂数据类型:如数组、指针、堆内存;
- 提升摘要精度:用更精准的代数程序分析技术(如非线性不变量)减少假阳性。