定义
程序验证>形式化方法>模型检测
模型检测Model Checking是形式化方法(formal method)的一种。主要思想是对系统的全局空间进行搜索,验证是否满足相关规范。
通过一些逻辑规则,对软件(program=data + logic)进行推理(reasoning)。因为涉及推理,所以会用到逻辑理论中的谓词,霍尔逻辑三元组之类的知识
目的
在开发过程中就进行检查,把软件错误控制在运行之前、运行早期,不要发生在生产环境,并不是亡羊补牢等出了错再去找错
应用场景
应用场景:工控软件、C语言程序、物联网场景、计算机硬件、通信协议、控制系统、安全认证协议、操作系统
自动化程序验证与修复 Automated Program Verification & Fixing
和常见动静态分析的关系
动态 运行中确定 fuzzing 覆盖执行路径而无法覆盖所有路径,无法证明整个程序 手动测试(如单元测试,Unit Testing,对软件中的最小可测试单元进行测试,如一个方法,是白盒,try catch、timeout) 模糊测试(Fuzzing Testing)
静态 静态分析、符号执行、模型检测
缺点:
模型检测方法的缺点是系统模型的建立需要领域专家的参与。 寻找恰当的抽象层次,从而足以证明系统的特定属性,是模型检测的一大难点。 过分的抽象将导致属性无法证明;而不足的抽象又将导致太多属性无关的冗余细节,从而引发状态爆炸,无法在合理的时间内得到结果。 经典的模型检测工具有NuSMV、SPIN等等。
程序验证处理一般语句——交换值
a = a + b;
b = a - b;
a = a - b;
代码->SSA静态单赋值程序->逻辑公式->用求解器证明正确
要验证的问题:交换了值,对应属性a会等于b的初始值b0,b会等于a的初始值a0
模型:对于任意的a和b,经过这段代码,属性都满足
自动化的依据:
从以上的过程中,我们可以发现,程序源码到SSA是可以自动化进行的; 从SSA到逻辑公式也是可以自动化进行的; 而证明逻辑公式的正确性,也可以借助于约束求解器来自动化实现。 所以,在程序验证中,理论上(似乎)我们只要提供程序源码和描述属性的断言,就可以通过自动化的方法,证明属性成立。
程序验证处理条件语句——按大小排列两个整数、assume
IF分支的SSA1 & ELSE分支的SS2,分别验证属性
程序验证处理循环语句——小于N的变量累加到N
循环无法直接转为逻辑公式。变量在循环执行的过程中,可能被反复赋值
用 循环不变式 代替循环效果
循环不变式验证数组越界——下标变量在下标范围内
谓词
命题:命题(proposition)就是非真即假的陈述句。
谓词是对命题的拆解
谓词逻辑公式包含量词、逻辑运算符,变量(Variable,以及谓词(Predicate,和函数(Function
A(x):表示为使其值为true的元素x的集合!!!!!!!!!,真假由x决定。使得谓词公式为真(可满足性、可满足性理论SMT)的一组变量解释
谓词是一种特殊的函数
谓词P(x)表示x是水果,可以将谓词解释为某种固定含义
谓词值域0,1,函数值域D
程序验证的上近似思想
基于对程序状态的抽象来进行, 即寻找一个程序实际状态的上近似,证明在这个上近似中,断言属性不会被违背
程序验证的理论基础——霍尔逻辑(Floyd-Hoare Logic)
在其基础上,我们可以在程序代码和谓词逻辑公式之间,建立起「等语义关系」的转化,从而确保我们的程序验证方法是有效的。
霍尔三元组
霍尔逻辑推理规则
空语句规则
赋值语句规则
顺序语句规则用于组合已经证明的三元组
条件分支语句 b是判断条件?是的,满足b且满足谓词P(类似if前),执行后都为Q,应该是if的后续基本快,P经过If语句变为Q
循环 循环前b满足,循环后b不满足,P是循环不变量,因为前后不变
前置条件强化规则 子集满足->父集满足
霍尔逻辑的一种推理形式——最弱前置条件(Weakest Pre-Condition)推理。
从后往前,最强后置条件从前置P开始
最弱前置条件表达了「经过程序c后使得Q成立的最大状态集合」前提条件的最宽泛范围 所以可以精细证明,可以范围证明 通过计算最弱前置条件的方式,来判断对应的霍尔三元组成立与否。
那么中间的步骤,也就是当前TAP下物联网的执行路径,就是c
弱必须是相对于另一个谓词而言的。可以把谓词P(x)理解成使P(x)为真的元素x的集合,同时把谓词之间的蕴含关系理解成集合的包含关系。 比如将P(x)->Q(x)理解为P(x)⊆Q(x)。 那么,我们说Q(x)比P(x)弱,当且仅当P(x)->Q(x)
P(x)⊆Q(x)都能成立,能成立的最低标准,再低就不成立了
计算得最弱前置条件为true,可以证明程序片段的正确性。
false已经是最强的一个谓词,它不能被任何其他非false的谓词蕴含。
从集合的观点来看,false表示空集。 这也就是说,不会有任何程序状态,经过程序片段能够达到后置条件。
插值——二元、归纳序列
https://zhuanlan.zhihu.com/p/332406712
这里的插值不是数学意义上的插值,而是在逻辑公式层面的插值
Craig插值
因为A→B,意思就是 符合A的情况下,B是真的。
常识不可以用来解释逻辑关系。
A和B插值表达的是A的一些关键性质,正是因为这些性质使得A、B矛盾。插值抽象掉了A中与矛盾无关的部分。
以程序分析为例,倘若A表示一条路径约束,B表示某一属性,那么,A∧B不可满足表示该路径中属性不可达,因为路径和属性无交集。 那么我们可以通过计算A和B的插值I,使用I来表示和该路径有相似原因导致属性不可达的路径集合,从而实现泛化,使我们能分析更多的路径。
二元插值 两个公式之间
序列插值
I使得F1到FK-1,FK到FN冲突?那么就对应上了
中缀表达式是符合我们人类的思维想法,要用到2个栈
前缀后缀只需要用到一个栈就可以解决 运算符放前面是前缀表达式
谓词抽象
整个计算过程都在抽象域所定义的抽象空间中进行,而不涉及程序的实际运行状态空间,从而能极大地提高计算效率。
LTL satisfiability solvers
研究论文
CCF-A 以前都是semi automated,它是第一篇,针对的是工控程序
基于谓词抽象的C语言程序安全性验证
形式化验证是保障系统无编程错误的唯一方法,但会造成空间爆炸
不采用全局方式存储谓词,而是将抽象状态的谓词映射到对应的状态节点上。
当验证过程被驳回时,不必回到初始状态而是回到某个状态
克雷格插值作为谓词应用到抽象模型中