如何使用Z3 Theorem Prover验证c++代码的正确性? (形式化验证)

13次阅读

Z3不能直接验证c++代码,因其是SMT求解器而非编译器或解析器;需手动将C++逻辑(如断言、循环不变量)翻译为SMT-LIB格式的逻辑公式再交由Z3求解。

如何使用Z3 Theorem Prover验证c++代码的正确性? (形式化验证)

Z3 不能直接验证 C++ 代码的正确性——它不解析 C++ 语法,也不执行或编译 C++ 程序。你要做的,是把 C++ 中的关键逻辑(比如循环不变量、函数前置/后置条件、断言)手动翻译成 Z3 能理解的逻辑公式,再交由 Z3 求解。

为什么不能直接输入 .cpp 文件给 Z3?

Z3 是一个 SMT 求解器,它处理的是已形式化的逻辑表达式(如:整数线性算术、位向量、数组、未解释函数等),不是源码分析器或编译器前端。它没有 C++ 词法分析器、语法树构建器或语义模型。

  • z3 命令行工具只接受 SMT-LIB v2 格式的文本(.smt2 文件)或通过 API 构造的表达式
  • C++ 的指针运算、内存布局、模板实例化、异常控制流等,在 Z3 中没有直接对应语义
  • 即使使用 Clang AST 提取逻辑,也必须由你定义“哪些行为需验证”并映射为约束(例如:将 for (int i = 0; i 显式建模为归纳变量 + 不变量断言)

典型流程:从 C++ 断言到 Z3 公式

以一个简单函数为例:

int abs_diff(int a, int b) {     return (a > b) ? a - b : b - a; }

你想验证:对所有整数 a, b,结果非负且等于 |a - b|

你需要做三件事:

  • 用 Z3 的 Int 类型声明两个变量:a, b
  • 定义输出变量 res,并添加分支等价约束:Or(And(a > b, res == a - b), And(a
  • 添加待验证性质作为否定后检查是否可满足(反证法):Not(res >= 0);若 Z3 返回 unsat,说明该性质恒成立

python + z3py 示例:

立即学习C++免费学习笔记(深入)”;

from z3 import * a, b, res = Int('a'), Int('b'), Int('res') s = Solver() s.add(Or(And(a > b, res == a - b), And(a <= b, res == b - a))) s.add(res < 0)  # 否定待证性质 print(s.check())  # 输出 unsat → 性质成立

常见陷阱与绕不开的取舍

实际工程中卡住的地方往往不是 Z3 用法,而是建模本身:

  • C++ 的未定义行为(如带符号整数溢出)在 Z3 中默认按数学整数处理;若要模拟 32 位补码,必须显式使用 BitVec(32) 并重写所有运算
  • 动态内存(new/delete)、函数指针、虚函数调用无法直接建模;只能抽象为未解释函数(function('malloc', IntSort(), IntSort())),但会大幅削弱验证力度
  • 循环必须人工归纳:Z3 不自动推导循环不变量;你得自己写出不变量公式,并用 Implies 表达“进入前成立 ⇒ 执行一次后仍成立”
  • 浮点数要用 FPSort(8, 24)(单精度)等,且需启用 fp 理论,而 C++ Float 的实际行为(如 FMA、舍入模式)仍需额外建模

真正能落地的场景,是小而关键的算法模块(如加密辅助函数、协议解析边界检查、ring buffer 索引计算),配合人工提取的规约。指望 Z3 “全自动验证整个 C++ 项目”,目前只是论文标题里的动词。

text=ZqhQzanResources