静态分析(static analysis)是PL(program languages)的一个分支。
PL的组成
PL由如下几个部分构成。
1. Theory
顾名思义,要想创建一个语言,你首先需要理论支持,即从头开始设计一门语言,它的类型系统,语义和逻辑等等
2. Environment
在设计好了语言之后,你需要实现一个编译器(能够将你的语言程序转换成计算机可运行程序),以及一个运行时环境(能够跑你的程序)。
3. application
在程序能够运行之后,我们想着这个东西可以用来干嘛(你造个语言出来肯定是给大家用的吧)。
比如可以用在静态分析(将要学习)
、程序的认证、程序合成等等方面。
static analysis
1. static analysis的作用
虽然一开始说它的作用可能有点不对,但是可以让米娜三了解到它的重要性。
静态分析有很多用处,比如用在 验证程序可靠性 上(空指针取消引用,内存泄漏啊),用在 程序的安全性 上(隐私信息泄露、注入攻击等等), 也可以用在 编译器优化 上,(比如消除死代码),还能用在 理解程序 上,(比如画出cfg,帮助理解代码逻辑等等,ida警告)
2. 什么是static analysis
static analysis 指的是 在一个程序A运行前能够了解到A的行为并确定它是否满足某些要求(比如安全性要求)
说的大白话一点,在你编写完一个程序后,你不知道你程序在运行时会发生什么错误,有静态分析这项技术,不用运行这个程序,你也可以获得关于这个程序的所有隐患(程序员狂喜,都不用debug了)。这不是爽歪歪!
然而现实是残酷的,根据Rice’s Theorem,完美的静态分析是不可能的
还是用上面的例子,静态分析这项技术其实并不能发现这个程序的所有隐患,但是,虽然我们没有完美的静态分析技术,但是我们可以推出有用的静态分析技术,虽然不能找到所有隐患,但是能找到60%的隐患也不错啊,也可以省下不少时间。
所以 有用的静态分析技术 成为了当前研究的热点。
3. sound和complete
sound和complete是当前静态分析的主要概念(非常重要)
我们用一张图形象的描述sound和complete在静态分析技术中的重要性。
说回上文,我们说过现在研究的是有用的静态分析技术,虽然不能百分百精准无误拿到有用信息(truth);但是,如果静态分析技术能够拿到所有有用信息,但是其中有冗余信息,就被称作是sound
;如果静态分析技术只能拿到部分有用信息,但是其中没有冗余信息,就被称作是complete
。
所以有用的静态分析技术衍生出了2个概念:
- compromise soundness(妥协sound),会造成 false negatives,漏报)
- compromise completeness(妥协complete,会造成 false positives,即误报)
当前趋势是第二种,即追求sound,妥协complete,允许误报,但是要拿到所有有用的信息。
这是为啥呢? 举个例子:
这个片段的代码是否是安全的?其实是不安全的,在到达B b'=(B) a.fld
前,有2种可能路径,其中一种是不安全的;如果漏报的话,有可能失去绿色箭头
所表示的那个路径,导致结论是 这个代码是安全的,这是我们不能接受的事情。
另外 soundness对于编译器优化和程序验证是至关重要的(从上面的例子就可以看出来)。
但是呢,百分百的soundness又不现实~,因为要分析所有可能出现的分支极其浪费时间(可能分析个1年不一定跑出结果来,这就很蛋疼了),所以在实际编写静态分析的工具时,往往需要平衡精度和分析速度。
4. 静态分析的构成
静态分析 = abstraction(抽象) + over-approximation(过近似)。
所谓abstraction,其实是将具体的值转换成某种符号 ,比如下面这个例子,我们要从具体的值中得到每个值的符号。
我们可以看到 v=e?1:-1
这种,v既可能是正数也可能是负数,因此是不确定的。 而v=w/0
这种就是错误表达。因此分别用unknow和undefined来表示
所谓over-aproximation,其实是根据问题的分析以及语义 制定规则。 比如:
现在再来个实际的例子: 从这个例子中,我们可以发现,静态分析在分析过程中会出现误报的问题