《软件分析》课程笔记(一)

  • 是否存在xx算法类证明:假设存在,构造反例
  • 莱斯定理:程序分析问题是不可判定问题
  • 上下近似、sound & complete

简介

软件的缺陷可能会造成非常严重的后果,如美加停电事故,特斯拉车祸等等。

那么人们可能会想,有没有办法可以彻底避免软件中出现缺陷呢?
由于对于“缺陷”的定义存在分歧(不是 bug 是 feature),所以笼统的讨论是没有意义的。在现实中,我们一般会给出一些可以明确定义的错误。

更确切的说:

  • 输入
    • 给定程序 P
    • 给定某种缺陷,比如内存泄漏
  • 输出
    • 是否存在算法能给出该判定问题的答案

希尔伯特计划 Hilbert’s Program

集合论出现之后,罗素提出了著名的罗素悖论:

R = { X ∣ X ∉ X } , R ∈ R ? R=\lbrace X | X \notin X\rbrace,R\in R ? R={XX/X},RR?
根据朴素集合论中的无限制概括公理对于任何一个性质,可以将满足该性质的所有元素构成一个集合。那么我们就能用 性质 来定义一个 集合
那么我们可以找到一个集合元素包括自己的集合,比如“包含无穷多元素的集合”的集合,因为包含了无穷多的元素,所以就包含了自身。
那么同样,我们想找到一个所有 元素不包括自己的集合 的集合。假设不包括自己,那么因为包含了所有不包括自己的集合,那么就包含了自己。假设包括自己,那么就与前提矛盾。这与:”理发师只给不给自己理发的人理发“非常相似。

为了解决这个问题,大卫·希尔伯特在20世纪20年代提出了希尔伯特计划(Hilbert’s Program)。
计划的目标是:提出一个形式系统,可以覆盖现在所有的定理,并具有如下特点:

  • 完备性:所有真命题都可以被证明
  • 一致性:不可能推出矛盾。即一个命题要么为真真,要么是假,不能既是真又是假。
  • 可判断性:存在一个算法确定任意命题的真假

可判定问题

所谓的判定问题(Decision Problem)指的是 回答是/否 的问题
而可判定问题(Decidable Problem),是一个判定问题,该判定问题存在要给判定算法,使得对于该问题的每一个实力都能给出 是/否 的答案。

停机问题是不可判定问题,确定程序有没有内存泄漏也是不可判定问题。

哥德尔不完备定理 Gödel’s Incompleteness Theorem

哥德尔在 1931 年证明:

包含自然数和基本算数运算(如四则运算)一致系统一定不完备,即包含一个无法证明的定理

对于主流编程语言,语法+语义=能表示自然数的形式系统。
那么设有表达式 T 不能被证明,对于下面的代码:

a = malloc();
if (T) free(a);
return;

如果 T 为永真式,那么没有内存泄漏,否则就会有内存泄漏。因为 T 无法判定,所以有没有内存泄漏也无法判定。

停机问题

停机问题说的是,判断一个程序在给定输入上是否会终止
这对应着希尔伯特期望的第三个属性,即判断任意一个命题的真假。

但是图灵在 1936 年给出证明:不存在一个算法能回答停机问题。因为当时没有计算机,所以图灵顺便提出来了图灵机。

停机问题证明

停机问题的证明与罗素悖论类似,都是构造一个自相矛盾的反例:

假设存在停机问题的判断算法bool Halt(p),其中 p 为给定输入的程序。如果可以停机,返回 true,否则返回 false。
那么我们可以构造一个”邪恶程序“:

void Evil() {
		if (Halt(Evil))  while(1);
		else return;
}

即如果算法判断 Evil 能够停机,那么我们就让 Evil 死循环,即 Evil 不停机,矛盾;如果判定算法说 Evil 不能停机,那么就返回,让 Evil 停机,矛盾。

内存泄漏算法存在性证明

通过同样的思路,我们可以证明没有内存泄漏的判定算法:

假设存在内存泄漏的判定算法bool MemLeakCheck(Program p)
构造如下程序:

void Evil() {
		p = malloc();
		if (MemLeakCheck(Evil))  return;
		else free(p);
}

莱斯定理(Rice’s Theorem)

那么究竟有多少程序分析问题是不可判定问题呢?

我们可以将任意程序看成从输入到输出上的函数(一个输入输出对的集合),该函数描述了函数的行为。
对于该函数/集合的任何 non-trivial 的属性,都不存在可以检查该属性的通用算法

  • trivial:要么对全体程序都为真,要么对所有程序都为假
  • non-trivial:不是 trivial 的所有属性

用莱斯定理可以快速判定一个问题是不是可判定问题。但有的问题不涉及莱斯定理定义,这种问题不代表不可判定,比如

给定无循环和函数调用的程序,判断程序是否在某些输入上会抛出异常

由于性质只涉及部分程序,而不是所有程序,所以不符合莱斯定理的范畴。但是不符合定义不代表不能判定。对于这个问题,只要实际跑一遍程序就可以了。

莱斯定理证明

定理证明与之前停机问题的思路相同,利用反证法构造反例。

给定函数上的 non-trivial 性质 P
假设空集(对任何输入都不输出的程序)不满足 P,另外,这里我们认为不停机的程序为空集,因为他对任何输入都不输出

  • 因为 P non-trivial,所以一定存在程序使得 P 得以满足,记为 ok_program
  • 加黑色检测该性质的算法名为 checkP
    编写如下函数检测 q 是否停机:
Program evil(Program q) {
		q();
		return ok_program;
}

bool halt(Program q) {
		return checkP(evil);
}

如果 q 不停机,那么程序 evil 为空集,checkP(evil)返回 false;如果 q 停机,那么 checkP(evial) 返回 true。这样就写出来了一个停机问题的判定算法,但停机问题是不可判定的,所以矛盾。

一个停机问题检测算法

提出一个停机问题检测算法:

  • 当前系统的状态为内存和寄存器中所有Bit的值
  • 给定任意状态,系统的下一状态是确定的
  • 令系统的所有可能的状态为节点,状态A可达状态B就添加一条A到B的边,那么形成一个有向图(有限状态自动机)
  • 如果从任意初始状态出发的路径都无环,那么系统一定停机,否则可能会死机
    • 给定起始状态,遍历执行路径,同时记录所有访问过的状态。
    • 如果有达到一个之前访问过的状态,则有环。如果达到终态,则无环。
  • 因为状态数量有穷,所以该算法一定终止

看上去好像没有什么问题,那么停机问题是可以判定的问题了吗?

void Evil() {
   	if (Halt(Evil))  while(1);
		else return;
}

这个检测算法运行时需要比 被检查程序 P 的状态 更多的状态,来存储程序 P 相关的信息。
也就是说,Halt(Evil) 需要比 Evil() 的运行需要更多状态,而 Evil() 的运行需要比 Halt(Evil) 更多的状态空间。所以 Halt(Evil) 无法运行。
但是对于一般的程序来说,不会由程序调用 Halt,所以对于这类程序,这个算法是可以工作的。

这种利用于有限状态自动机抽象判断程序属性的技术叫做 Model Checking,被广泛应用于硬件领域,在软件领域因为状态爆炸问题(即状态数太多),几乎无法被应用到大型程序上。

所以,在软件上,提出了近似法

近似求解判定问题

原始问题要求输出 ”是“ 或者 ”否“,而近似求解判定问题可以输出”是“,”否“和”不知道“。

在之后,出现了两个变体:

  • 只输出”是“或者”不知道“
    • must analysis, lower/under approximation(下近似)
  • 只输出“否”或者“不知道”
    • may analysis, upper/over approximation(上近似)

而研究的目标就是尽可能多的回答”是“或者”否“,否则都回答”不知道“那么一个工具就做完了。

在这里插入图片描述
对于内存泄漏问题来说,上近似指的是,如果说没有内存泄漏,那么一定没有;但是说不知道,则有可能由内存泄漏;而下近似指的是,如果说有,那么一定有内存泄漏。

非判定问题

近似方法中的 must 和 may 分析的定义取决于问题性质。

假设正确答案是一个集合 S,那么 must 分析就是返回的集合是 S 的子集;may 分析返回的是 S 的超集;或者更全面的分析,返回不相交(Disjoint)集合MUST,MAY,NEVER,其中:

  • MUST⊆S
  • NEVER∩S=∅
  • S⊆MUST∪MAY

may 和 must 的区分并不严格,可以相互转换。比如将判定问题取法,对于返回集合的问题,将返回值定义为原集合的补集。

健壮性 soundness

程序分析技术最早源自编译器优化,在编译器优化中,我们需要保证决定不改变程序的语义。这是,人们提出健壮性表示分析结果保证不会改变程序语义。

  • 健壮性的定义和具体应用场景有关,但往往对应于must分析和may分析中的一个。
  • 健壮性有时也被成为安全性(Safety)、正确性(correctness)。
  • 健壮性的反面有时也被称为完整性(completeness)。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值