Bit-Vector框架(1)——Reaching Definition Analysis
以下是4个Bit-Vector Framework
的数据流分析经典例子。
Reaching Definition Analysis(本文)
:变量/数组在哪里最新被定义它们的值- Live Variables Analysis:变量/数组在重新被定义之前(或者程序结束之前),是否被使用
- Available Expressions Analysis:哪些表达式之前已经被计算过,当再次进行计算时能够得到相同的值
- Very Busy Expressions Analysis:哪些表达式在哪些点被计算后,程序结束前仍然被计算,并且计算的结果相等。
1. Reaching Definitions
1.1 Reaching Definition简介
RD分析求解的信息
- 求解程序图中的
每个节点处
,变量/数组最后一次
的可能被定义/修改
的地方。
例子(图2.1 中的q1处)
- 变量y可能被修改的行为有两处:
q0 -> q1
和q2 -> q3
。 - 变量x则为:
q3 -> q1
和? -> q0
(x可能没有被修改,我们用? -> q0
表示)
RD求解信息的用途
-
编译优化 和 程序理解
-
连接
变量被定义的地方
和被使用的地方
。 -
验证安全属性
-
检查变量是否被合适地初始化,进而知道程序可能存在某种形式的可被攻击的缺陷
例如,下图是一个最简单的检测PHP程序中XSS代码缺陷的例子:
1.2 分析结果的定义
Q = { 程 序 图 所 有 节 点 的 集 合 } , 图 示 Q = { q 0 , q x , q 1 , q 2 , q 3 } A c t = { 程 序 中 所 有 语 句 行 为 的 集 合 } , 图 示 A c t = { y : = 1 , x < = 0 , x > 0 , y : = x ∗ y , x : = x − 1 } E d g e = { 程 序 中 所 有 边 的 端 点 以 及 它 们 上 行 为 的 集 合 } , 图 示 E d g e = { ( q 0 , y : = 1 , q 1 ) , ( q 1 , x < = 0 , q x ) , ( q 1 , x > 0 , q 2 ) , ( q 2 , y : = x ∗ y , q 3 ) , ( q 3 , x : = x − 1 , q 1 ) } V a r = { 程 序 中 所 有 简 单 变 量 的 集 合 } , 图 示 V a r = { x , y } A r r = { 程 序 中 所 有 数 组 变 量 的 集 合 } , 图 示 A r r = { } 定 义 三 元 组 : ( x , q s , q t ) , ( A , q s , q t ) 其 中 : ( q s , q t ) ∈ E d g e x ∈ V a r A ∈ A r r \begin{aligned} Q&=\{程序图所有节点的集合\}, \\ 图示Q&=\{q_0,q_x,q_1,q_2,q_3\} \\ \\ Act&=\{程序中所有语句行为的集合\},\\ 图示Act&=\{y:=1,x<=0,x>0,y:=x*y,x:=x-1\} \\ \\ Edge&=\{程序中所有边的端点以及它们上行为的集合\}, \\ 图示Edge&=\{(q_0,y:=1,q_1),(q_1,x<=0,q_x),(q_1,x>0,q_2),(q_2,y:=x*y,q_3),(q_3,x:=x-1,q_1)\}\\ \\ Var&=\{程序中所有简单变量的集合\}, \\ 图示Var&= \{x,y\}\\ \\ Arr&=\{程序中所有数组变量的集合\}, \\ 图示Arr&=\{\} \\ \\ 定义三元组&: (x, q_s, q_t), \ \ (A, q_s, q_t) \\ 其中& \ :(q_s, q_t) \in Edge \\ & \ \ \ \ x \in Var \\ & \ \ \ \ A \in Arr \end{aligned} Q图示QAct图示ActEdge图示EdgeVar图示VarArr图示Arr定义三元组其中={程序图所有节点的集合},={q0,qx,q1,q2,q3}={程序中所有语句行为的集合},={y:=1,x<=0,x>0,y:=x∗y,x:=x−1}={程序中所有边的端点以及它们上行为的集合},={(q0,y:=1,q1),(q1,x<=0,qx),(q1,x>0,q2),(q2,y:=x∗y,q3),(q3,x:=x−1,q1)}={程序中所有简单变量的集合},={x,y}={程序中所有数组变量的集合},={}:(x,qs,qt), (A,qs,qt) :(qs,qt)∈Edge x∈Var A∈Arr
三元组(x, qs, qt),(A, qs, qt)
表示变量x或者数组A
最新被定义可能是通过一条边为(qs,act,qt)上的act
行为所定义/修改的。我们允许让qs为?, 表示x或者A仍然保持初值不变
。
RD映射程序图中每个节点到形如三元组<x, qs, qt>或者<A, qs, qt>
的集合。并且qs可能为?。RD函数的形式化定义为:
R
D
:
Q
→
P
o
w
e
r
S
e
t
(
(
V
a
r
∪
A
r
r
)
×
Q
q
×
Q
)
其
中
:
Q
q
=
{
?
}
∪
Q
\begin{aligned} RD &: Q \rightarrow PowerSet(\ (Var\cup Arr) \times Qq \times \ Q) \\ 其中&: Qq = \{?\} \cup Q \end{aligned}
RD其中:Q→PowerSet( (Var∪Arr)×Qq× Q):Qq={?}∪Q
如果关心平行边的话,可以考虑:
R
D
:
Q
→
P
o
w
e
r
S
e
t
(
(
V
a
r
∪
A
r
r
)
×
Q
q
×
A
c
t
×
Q
)
RD:Q \rightarrow PowerSet( (Var \cup Arr) \times Qq \times Act \times Q )
RD:Q→PowerSet((Var∪Arr)×Qq×Act×Q)
我们并不关心平行边。
所以不予考虑。
1.3 路径上定义了什么
1.3.1 定义程序单个行为的语义函数
首先分析对于每个行为act,数组/变量会被怎么定义/修改:
D
e
f
(
x
:
=
a
)
=
{
x
}
D
e
f
(
A
[
a
1
]
:
=
a
2
)
=
{
A
}
D
e
f
(
c
?
x
)
=
{
x
}
D
e
f
(
c
?
A
[
a
]
)
=
{
A
}
D
e
f
(
c
!
a
)
=
{
}
D
e
f
(
b
)
=
{
}
D
e
f
(
s
k
i
p
)
=
{
}
\begin{aligned} &Def(x := a) &= &\{x\} \\ &Def(A[a_1] := a_2) &= &\{A\} \\ &Def(c?x) &= &\{x\} \\ &Def(c?A[a]) &= &\{A\} \\ &Def(c!a) &= &\{\} \\ &Def(b) &= &\{\} \\ &Def(skip) &= &\{\} \\ \end{aligned}
Def(x:=a)Def(A[a1]:=a2)Def(c?x)Def(c?A[a])Def(c!a)Def(b)Def(skip)======={x}{A}{x}{A}{}{}{}
其中,对于数组元素的修改,我们近似地认为修改数组整体。
接着我们分析对于一条路径,数组/变量会被怎么定义/修改。
1.3.2 一条路径上变量x的定义函数
下面定义:对于一条路径Pi
和一个变量x
,我们定义Pi如下:
P
i
=
q
0
,
a
1
,
q
1
,
a
2
,
q
2
,
.
.
.
,
q
n
−
1
,
a
n
,
q
n
其
中
,
n
≥
0
q
i
∈
Q
,
0
≤
i
<
n
a
j
∈
A
c
t
,
1
≤
j
≤
n
\begin{aligned} Pi = &\ q_0, a_1, q_1, a_2, q_2, ..., q_{n-1}, a_n,q_n \\ 其中,&n \geq 0 \\ &q_i \in Q, 0 \leq i < n\\ &a_j \in Act, 1 \leq j \leq n \end{aligned}
Pi=其中, q0,a1,q1,a2,q2,...,qn−1,an,qnn≥0qi∈Q,0≤i<naj∈Act,1≤j≤n
定义一条路径上变量x的Def函数
为:
D
e
f
(
P
i
,
x
)
=
{
(
x
,
q
i
−
1
,
q
i
)
如
果
x
∈
D
e
f
(
a
i
)
,
并
且
∀
j
>
i
:
x
∉
D
e
f
(
a
j
)
(
x
,
?
,
q
0
)
如
果
∀
j
:
x
∉
D
e
f
(
a
j
)
Def(Pi,x)= \begin{cases} (x,q_{i-1},q_{i})& 如果x\in Def(a_i),并且\forall j>i:x\notin Def(a_j) \\ (x,?,q_0) & 如果\forall j:x\notin Def(a_j) \end{cases}
Def(Pi,x)={(x,qi−1,qi)(x,?,q0)如果x∈Def(ai),并且∀j>i:x∈/Def(aj)如果∀j:x∈/Def(aj)
上面对变量x的Def函数的意思很简单:
在一条路径结束,x会被路径上最后一个定义它的程序行为来定义;
如果路径中不存在任何定义x的点,那么它保持着最初的值,对应的定义点就是(x,?,q0)。
1.3.3 一条路径上数组A的定义函数
而对于数组变量,Def(Pi, A)函数的定义为:
D
e
f
(
P
i
,
A
)
=
{
(
A
,
?
,
q
0
)
}
∪
{
(
A
,
q
i
−
1
,
q
i
)
∣
A
∈
D
e
f
(
a
i
)
}
Def(Pi, A) = \{\ (A, ?, q_0)\ \} \ \cup \ \{\ (A,q_{i-1},q_i) \ \ | \ \ A \in Def(a_i) \} \\
Def(Pi,A)={ (A,?,q0) } ∪ { (A,qi−1,qi) ∣ A∈Def(ai)}
对数组A的Def函数的意思也很容易理解:
数组A仍然有部分元素保持最初的值,也就是数组仍然可以被认为被定义在程序的开始处:(A, ?, q0)
路径Pi上每次对A的定义我们都可以认为是数组A的定义点
1.3.4 总结
综上,对于一条路径Pi,它的定义函数为:
D
e
f
(
P
i
)
=
{
D
e
f
(
P
i
,
x
)
∣
x
∈
V
a
r
}
∪
⋃
A
∈
A
r
r
D
e
f
(
P
i
,
A
)
Def(Pi) = \{\ Def(Pi, x)\ |\ x\in Var \ \} \ \cup \ \bigcup_{A\in Arr}Def(Pi, A)
Def(Pi)={ Def(Pi,x) ∣ x∈Var } ∪ A∈Arr⋃Def(Pi,A)
也就是说,一条路径的定义函数为:对变量和数组的定义之和
通过上面的函数概念,我们现在可以重新定义Reaching Definition Analysis:
对于程序图上的每个节点q,我们想求解出所有可能的,从q0到q的路径上产生的定义,之和。
其中q0为程序的入口点,q为除q0外所有的程序点。
而我们知道,因为程序存在循环的情况,静态分析的情况下很多时候是求解不出所有的执行路径。但是,存在一些路径,它们已经能够足够地给出程序点的所有可能的定义。能够总结出程序点的所有路径的定义的最小(least)分析结果,我们也称之为
MOP解(Merge Over Paths solution)
1.4 RD分析的约束
1.3是通过将所有路径的定义结果进行merge合并,这并不是一种有效的计算正确的RD分析的方法。因为很多时候我们静态地并不能够分析出所有的可能执行路径,所以很难将所有路径的结果进行merge。
为了改进上述方法。现在,我们定义一个约束系统,RD必须满足,之后我们会给出算法来求解这些约束。
在定义约束系统之前,我们先定义一下约束系统中的约束因子:KILL和GEN
对于程序图中每条边(qs, a, qt)
,我们定义所谓的对定义的KILL函数和GEN函数:
a | KILL(q s _s s, a, q t _t t) | GEN(q s _s s, a, q t _t t) | 解释 |
---|---|---|---|
x := a | {x} × \times × Q ? _? ? × \times × Q | {x, q s _s s, q t _t t} | 1. 此边会Kill掉所有定义x的点;并在此边上生成x的定义 |
A[a1] := a2 | {} | {A, q s _s s, q t _t t} | 2. 保守分析,不Kill掉数组;但是会在边上生成A的定义 |
c?x | {x} × \times × Q ? _? ? × \times × Q | {x, q s _s s, q t _t t} | 同1 |
c?A[a] | {} | {A, q s _s s, q t _t t} | 同2 |
c!a | {} | {} | 没有赋值的语义,与Kill和Gen没关系 |
b | {} | {} | 同上 |
Skip | {} | {} | 同上 |
KILL,GEN的目的是为RD分析的约束服务。现在我们定义约束:
对于程序图上的每条边edge(qs, a, qt),必须满足:
∀ ( q s , a , q t ) ∈ E d g e R D ( q t ) ⊇ ( R D ( q s ) − K I L L ( q s , a , q t ) ) ∪ G E N ( q s , a , q t ) \begin{aligned} \forall (q_s, a, q_t)\ &\in \ Edge\\ RD(q_t) \ &\supseteq \ (\ RD(q_s) -KILL(q_s, a, q_t)\ ) \ \cup \ GEN(q_s, a, q_t) \end{aligned} ∀(qs,a,qt) RD(qt) ∈ Edge⊇ ( RD(qs)−KILL(qs,a,qt) ) ∪ GEN(qs,a,qt)
对于程序入口节点
q 0 _0 0 ,必须满足:
R D ( q 0 ) = ( V a r ∪ A r r ) × ( ? ) × { q 0 } RD(q_0) \ = \ (Var \ \cup \ Arr ) \ \times \ (?) \ \times \ \{q_0\} RD(q0) = (Var ∪ Arr) × (?) × {q0}
前者表示:从边(qs, a, qt)的一端qs传递过来的RD信息可能会通过该边的a行为kill掉部分定义。于此同时a行为又会产生定义。
约束条件中存在一个子集的概念,也就是说,无论qt的前驱节点是哪个qs,右侧求出的集合一定是RD(qt)的子集。因此,可以这么认为:对qt的所有前驱qs,求解右侧的集合运算,并将这些计算结果并集,就会得到RD(qt)。
后者表示:所有变量/数组有初始值。被定义的地方为一条虚拟边
? -> q0
,这是求解约束的初始状态
。
与MOP的定义类似,我们通过上面的约束系统,来重新定义Reaching Definition Analysis:
Reaching Definition Analysis就是要根据约束来求解出每个程序点的RD函数值,上述约束条件要在任何时候(等同于任何执行路径)都满足
1.5 求解约束
输 入 : 集 合 Q 包 含 程 序 图 中 所 有 节 点 ; 初 始 节 点 q 0 ; 边 集 合 E 输 出 : R D : R e a c h i n g D e f i n i t i o n A n a l y s i s 分 析 的 赋 值 定 义 结 果 函 数 ; 给 定 一 个 程 序 点 q , R D ( q ) 表 示 可 能 有 哪 些 定 义 可 达 程 序 点 q 算 法 : 对 于 所 有 q ∈ ( Q − { q 0 } ) , 令 R D ( q ) : = { } R D ( q 0 ) : = ( V a r ∪ A r r ) × { ? } × { q 0 } W h i l e 仍 然 存 在 ( q s , a , q t ) ∈ E , 使 得 R D ( q t ) ⊉ ( R D ( q s ) − K I L L ( q s , a , q t ) ) ∪ G E N ( q s , a , q t ) 那 么 令 R D ( q t ) : = R D ( q t ) ∪ ( R D ( q s ) − K I L L ( q s , a , q t ) ) ∪ G E N ( q s , a , q t ) \begin{aligned} 输入:&集合Q包含程序图中所有节点;初始节点q_0;边集合E \\ 输出:&RD: Reaching \ Definition \ Analysis分析的赋值定义结果函数;给定一个程序点q,RD(q)表示可能有哪些定义可达程序点q \\ 算法:& \\ &对于所有q \in (Q- \{q_0\}),令RD(q):=\{\} \\ &RD(q_0) := (Var \ \cup \ Arr) \ \times \ \{?\} \ \times \ \{q_0\} \\ &While \ 仍然存在(q_s,a,q_t) \in E,使得 \ RD(q_t)\not\supseteq(\ RD(q_s)-KILL(q_s,a,q_t)\ ) \ \cup \ GEN(q_s,a,q_t) \\ &\ \ \ \ \ \ \ \ \ \ \ \ 那么令 \ RD(q_t) \ := \ RD(q_t) \ \cup \ (\ RD(q_s)-KILL(q_s,a,q_t)\ ) \ \cup \ GEN(q_s,a,q_t) \end{aligned} 输入:输出:算法:集合Q包含程序图中所有节点;初始节点q0;边集合ERD:Reaching Definition Analysis分析的赋值定义结果函数;给定一个程序点q,RD(q)表示可能有哪些定义可达程序点q对于所有q∈(Q−{q0}),令RD(q):={}RD(q0):=(Var ∪ Arr) × {?} × {q0}While 仍然存在(qs,a,qt)∈E,使得 RD(qt)⊇( RD(qs)−KILL(qs,a,qt) ) ∪ GEN(qs,a,qt) 那么令 RD(qt) := RD(qt) ∪ ( RD(qs)−KILL(qs,a,qt) ) ∪ GEN(qs,a,qt)
算法的核心就在于while循环迭代,直至约束系统达到稳定状态。
核心想法就是,如果对于程序点q,仍然存在其它程序点的新的赋值定义流到q,那么就将新的赋值定义合并到程序点q。
对每个程序点都如此操作,最终能够通过上述约束等式,求解出每个程序点赋值定义可达的解。