程序验证工具的研究
description
Transcript of 程序验证工具的研究
报 告 提 纲• 一类基于逻辑推理的程序验证器的工作原理
– 通过一个简单例子了解概念和工作流程函数前后条件、循环不变式、 Hoare 逻辑的赋值公理,最弱前条件演算、验证条件的生成、验证条件的证明
• 面向实际程序验证需要解决的问题• 提高程序验证能力的思路• 一个程序验证系统原型演示
一类程序验证器的工作原理
function mult(m, n) { x = 0 ; y = 0 ;
while y < n do { x = x + m ;
y = y + 1 ;}
}
例子:用加运算来实现乘运算的函数
一类程序验证器的工作原理n 0 - - 前条件function mult(m, n) {
x = 0 ; y = 0 ;
while y < n do { x = x + m ;
y = y + 1 ;}
} x = m n - - 后条件
由程序员提供由程序员提供
由程序员提供由程序员提供
一类程序验证器的工作原理n 0function mult(m, n) {
x = 0 ; y = 0 ;
while y < n do { (x = my) (y n) - - 循环不变式
x = x + m ;
y = y + 1 ;}
} x = m n也由程序员提供
一类程序验证器的工作原理n 0function mult(m, n) {
x = 0 ; y = 0 ;
while y < n do { (x = my) (y n) - - 循环不变式
x = x + m ;
y = y + 1 ;}
} x = m n
函数前后条件、循环不变式都称为断言
它们看上去像编程语言的布尔表达式
一类程序验证器的工作原理n 0function mult(m, n) {
x = 0 ; y = 0 ;
while y < n do { (x = my) (y n)
x = x + m ;
y = y + 1 ;}
} x = m n
Hoare 逻辑赋值公理 :
{ Q[E/x] } x = E { Q }
例子 : { ? } x = x +1 { x > 6 }
{ x > 5 } x = x +1 { x > 6 }
x > 5 是 赋值 x = x +1, 和 后条件 x > 6
的最弱前条件
一类程序验证器的工作原理n 0function mult(m, n) {
x = 0 ; y = 0 ;
while y < n do { (x = my) (y n) x = x + m ;
y = y + 1 ;}
} x = m n
从后向前进行最弱前条件演算,产生验证条件
一类程序验证器的工作原理n 0function mult(m, n) {
x = 0 ; y = 0 ;
while y < n do { (x = my) (y n) x = x + m ;
y = y + 1 ;} ((x = my) (y n)) (y < n) // 循环结束程序点的断言
} x = m n
一类程序验证器的工作原理n 0function mult(m, n) {
x = 0 ; y = 0 ;
while y < n do { (x = my) (y n) x = x + m ;
y = y + 1 ;} ((x = my) (y n)) (y < n) (x = mn)
} x = m n
第 1 个验证条件
一类程序验证器的工作原理n 0function mult(m, n) {
x = 0 ; y = 0 ;
while y < n do { (x = my) (y n) x = x + m ;(x = m(y+1)) ((y+1) n)y = y + 1 ;
} (x = my) (y n) ((x = my) (y n)) (y < n) (x = mn)
} x = m n
通过最弱前条件演算得到
一类程序验证器的工作原理n 0function mult(m, n) {
x = 0 ; y = 0 ;
while y < n do { (x = my) (y n) (x+m = m(y+1)) ((y+1) n) x = x + m ;(x = m(y+1)) ((y+1) n) y = y + 1 ;
} (x = my) (y n) ((x = my) (y n)) (y < n) (x = mn)
} x = m n
一类程序验证器的工作原理n 0function mult(m, n) {
x = 0 ; y = 0 ;
((x = my) (y n)) (y < n) (x+m = m(y+1)) ((y+1) n)while y < n do { (x = my) (y n)
(x+m = m(y+1)) ((y+1) n) x = x + m ;(x = m(y+1)) ((y+1) n) y = y + 1 ;
} (x = my) (y n) ((x = my) (y n)) (y < n) (x = mn)
} x = m n
第 2 个验证条件
一类程序验证器的工作原理n 0function mult(m, n) {
x = 0 ;(x = m0) (0 n) y = 0 ;
((x = my) (y n)) (y < n) (x+m = m(y+1)) ((y+1) n)while y < n do { (x = my) (y n)
(x+m = m(y+1)) ((y+1) n) x = x + m ;(x = m(y+1)) ((y+1) n) y = y + 1 ;
} (x = my) (y n) ((x = my) (y n)) (y < n) (x = mn)
} x = m n
一类程序验证器的工作原理n 0function mult(m, n) {
(0 = m0) (0 n) x = 0 ;(x = m0) (0 n) y = 0 ;
((x = my) (y n)) (y < n) (x+m = m(y+1)) ((y+1) n)while y < n do { (x = my) (y n)
(x+m = m(y+1)) ((y+1) n) x = x + m ;(x = m(y+1)) ((y+1) n) y = y + 1 ;
} (x = my) (y n) ((x = my) (y n)) (y < n) (x = mn)
} x = m n
一类程序验证器的工作原理n 0function mult(m, n) { ( n 0 ) ((0 = m0) (0 n))
(0 = m0) (0 n) x = 0 ;(x = m0) (0 n) y = 0 ;
((x = my) (y n)) (y < n) (x+m = m(y+1)) ((y+1) n)while y < n do { (x = my) (y n)
(x+m = m(y+1)) ((y+1) n) x = x + m ;(x = m(y+1)) ((y+1) n) y = y + 1 ;
} (x = my) (y n) ((x = my) (y n)) (y < n) (x = mn)
} x = m n
第 3 个验证条件
一类程序验证器的工作原理n 0function mult(m, n) { ( n 0 ) ((0 = m0) (0 n))
x = 0 ;
y = 0 ;((x = my) (y n)) (y < n) (x+m = m(y+1)) ((y+1) n)
while y < n do { (x = my) (y n)
x = x + m ;
y = y + 1 ;} ((x = my) (y n)) (y < n) (x = mn)
} x = m n
由自动定理证明器由自动定理证明器完成验证条件的证明完成验证条件的证明
一类程序验证器的工作原理• 程序验证器小结
1 、程序员– 为各函数写函数前后条件– 为各循环写循环不变式2 、程序验证器– 为各函数产生验证条件(通过从前向后的演算或
从后向前的演算)– 证明所产生的验证条件(使用自动定理证明器)
报 告 提 纲• 一类基于逻辑推理的程序验证器的工作原理• 面向实际程序验证需要解决的问题
– 提高断言语言的表达能力– 解决各种数据类型带来的特殊问题– 解决非结构化控制结构带来的问题– … …– 提高自动定理证明器的能力
• 提高程序验证能力的思路• 一个程序验证系统原型演示
提高断言语言的表达能力• 例 1 :表达数组 a[100] 的有序性
– 可以在语言布尔表达式语法的基础上引入量词i:0..98. a[i] <= a[i+1]
• 例 2 :表达单链表的有序性– 用量词的方式
n:.i:0..n-2.p(->next)i->data <= p(->next)i+1->data
– 用归纳谓词的方式SortedList(p) p == NULL || p->next == NULL ||
p->data <= p->next->data &&SortedList(p->next)
提高断言语言的表达能力• 例 3 :表达二叉排序树的有序性
– 用归纳谓词的方式 Gt(x, p) p == NULL ||
x > p->data && Gt(x, p->left) && Gt(x, p->right)
Lt(x, p) p == NULL x < p->data && Lt(x, p->left) && Lt(x, p-
>right)
Bst(p) p == NULL Bst(p->left) && Bst(p->right) &&Gt(p->data, p->left) &&Lt(p->data, p->right)
– 用量词方式较难定义
提高断言语言的表达能力• 例 4 :断言语言需要更加复杂扩展的例子
– 程序不会出现内存泄漏– 程序不会对 dangling 指针进行 dereference 操作
– 在任何时候,最多只有一个进程处于临界区(安全性)
– 只要进程请求进入临界区,则最终会被允许进入(活性)
• 断言语言越复杂,生成的验证条件就复杂,验证条件的自动证明就越困难
解决各种数据类型带来的特殊问题以指针类型为例• 指针别名
{ A } x = 5 { x + y == 10 }– 按 Hoare逻辑的赋值公理
A = (x + y == 10) [5/x] = 5 + y == 10 = y == 5
– 实际上最弱前条件 A应该是: x == y || y == 5
• 先前提到的无内存泄漏等最终也导致对自动定理证明器有较高期望
解决非结构化控制结构带来的问题• Hoare逻辑对结构化语句的推理规则
• 循环体中的 break语句和 continue语句推理规则不会像上述规则这样简洁
{P} S1 {R} {R} S2 {Q}
{P} S1; S2 {Q}
{PE} S1 {Q} {PE} S2 {Q}
{P} if E then S1 else S2 {Q}
{PE} S {P}
{P} while E do S {PE}
报 告 提 纲• 一类基于逻辑推理的程序验证器的工作原理• 面向实际程序验证需要解决的问题
– 提高断言语言的表达能力– 解决各种数据类型带来的特殊问题– 解决非结构化控制结构带来的问题– … …– 提高自动定理证明器的能力
• 提高程序验证能力的思路• 一个程序验证系统原型演示
报 告 提 纲• 一类基于逻辑推理的程序验证器的工作原理• 面向实际程序验证需要解决的问题• 提高程序验证能力的思路
– 程序员提供对验证有帮助的提示– 提高合法程序的门槛– 用程序分析获得的信息来支持程序验证– … …
• 一个程序验证系统原型演示
程序员提供对验证有帮助的提示• 方式 1 :程序员声明数据结构的形状
– typedef struct node{Node* : LIST next; int data;} Node;
– typedef struct node{Node*:TREE left; Node* :TREE right; int data;}Node;
• 给程序员带来好处– 免除提供有关数据结构形状的函数前后条件– 免除提供有关形状的循环不变式– 帮助检查引起内存泄漏的操作– 帮助检查对 NULL 指针和 dangling 指针的
dereference 操作
程序员提供对验证有帮助的提示• 方式 2 :提供数据结构的性质定理
– 删除二叉排序树根节点的函数通过循环,找到左子树的最右叶节点,取它的数据作为根节点数据,删除该最右叶节点
– 函数的前条件是p!=NULL Bst(p)
– 函数的后条件式是Bst(p)
– 从 Bst(p) 等谓词定义 , 基于演绎推理 , 推不出性质:绿色节点是左子树上最大但又小于根的节点
… …
…
…
…
…
p
程序员提供对验证有帮助的提示• 方式 2 :提供数据结构的性质定理
Gt(x, p) p == NULL || x > p->data && Gt(x, p->left) && Gt(x, p->right)
Lt(x, p) p == NULL x < p->data && Lt(x, p->left) && Lt(x, p->right)
Bst(p) p == NULL Bst(p->left) && Gt(p->data, p->left) &&Bst(p->right) && Lt(p->data, p->right)
– 那是一个需要归纳证明的性质– 程序员在给出 Bst 等谓词定义时,
还需要给出程序用到的性质定理• 好处:减轻了自动定理证明器的负担
… …
…
…
…
…
p
提高合法程序的门槛• 方法 1 :给编程语言设计一个形状系统
排除不符合形状系统规定的程序,类似于类型检查– 单链表指针 h 作为函数的实参
h nxt . . .
m 个节点 , m>=0
nxtnxt 允许作为实参
指向它的是 NULL 指针
提高合法程序的门槛• 方法 1 :给编程语言设计一个形状系统
排除不符合形状系统规定的程序,类似于类型检查– 单链表指针 h 作为函数的实参
h nxt . . .
m 个节点 , m>=0
nxtnxt
h nxt . . .
m 个节点 , m>=0
nxtnxt
指向它的是 dangling 指针
允许作为实参
不允许作为实参
提高合法程序的门槛• 方法 1 :给编程语言设计一个形状系统
排除不符合形状系统规定的程序,类似于类型检查– 单链表指针 h 作为函数的实参
h nxt . . .
m 个节点 , m>=0
nxtnxt
h nxt . . .
m 个节点 , m > 0
nxtnxt nxt nxt . . . nxtnxtp
n 个节点 , n >0
h nxt . . .
m 个节点 , m>=0
nxtnxt
允许作为实参
不允许作为实参
h 不允许作为实参p 允许作为实参
提高合法程序的门槛• 方法 1 :给编程语言设计一个形状系统
排除不符合形状系统规定的程序,类似于类型检查
一个双向链表 h
r r. . .
rrl l l l
rl
循环迭代依次把域 l 都赋值为 NULL ,然后当单链表用
h
r r. . .
rr
l l l l
r
形状系统拒绝这个程序,因为它偏离了声明的形状
提高合法程序的门槛• 方法 1 :给编程语言设计一个形状系统
好处:– 有助于自动推断有关形状的循环不变式– 减轻自动定理证明器的负担
• 其他方法,例如– 对有些类型的运算加以限制:有控制地使用指针
算术运算和类型强制等– 减少非结构化的控制流
用程序分析来支持程序验证• 方法:先程序分析,后程序验证
用特定程序分析获取的信息来支持随后的程序验证例如
{ p->data == 10 }p1->nxt->data = p->data + 5
{ ? }
取决于 p1->nxt 是否等于 p。若不知道,则需要考虑两种情况,因而后条件是 :
p1->nxt != p (p->data == 10 p1->nxt->data == 15) p1->nxt == p p->data == 15
若先指针分析,得到指针相等信息,则大大简化
用程序分析来支持程序验证• 方法:先程序分析,后程序验证
用特定程序分析获取的信息来支持随后的程序验证例如
{ p->data == 10 }p1->nxt->data = p->data + 5
{ ? }
h nxt . . .
m 个节点 , m>=0
nxtnxt nxt nxt . . . nxtnxtp1 p
n 个节点 , n>=0
用程序分析来支持程序验证• 方法:先程序分析,后程序验证
用特定程序分析获取的信息来支持随后的程序验证例如
{ p->data == 10 }p->data = p->data + 5
{ ? }
h nxt . . .
m 个节点 , m>=0
nxtnxt nxt nxt . . . nxtnxtp1 p
n 个节点 , n>=0
用程序分析来支持程序验证• 方法:先程序分析,后程序验证
用特定程序分析获取的信息来支持随后的程序验证例如
{ p->data == 10 }p->data = p->data + 5
{p->data == 15 }
h nxt . . .
m 个节点 , m>=0
nxtnxt nxt nxt . . . nxtnxtp1 p
n 个节点 , n>=0
报 告 提 纲• 一类基于逻辑推理的程序验证器的工作原理• 面向实际程序验证需要解决的问题• 提高程序验证能力的思路
– 程序员提供对验证有帮助的提示– 提高合法程序的门槛– 用程序分析获得的信息来支持程序验证– … …
• 一个程序验证系统原型演示
报 告 提 纲• 一类基于逻辑推理的程序验证器的工作原理• 面向实际程序验证需要解决的问题• 提高程序验证能力的思路• 一个程序验证系统原型演示
一个程序验证系统原型演示• 特点
– 研究指针程序验证的一个成果,还在扩展完善中– 为操作易变数据结构的指针程序设计了一种专用
的形状图逻辑,设计了形状系统– 先形状分析,后程序验证
• 已经能验证– 演示:快速排序程序(数组程序)、二叉排序树
的插入函数(递归),二叉排序树的删除函数(循环)和树堆 (treap)
– 还有:伸展树 (splay tree) ,平衡树 (AVL tree) 和 AA tree 等
谢 谢 !