这篇文章来自 ECOOP'16,是 SPDS 的作者的先前工作,Boomerang 也有一个 SPDS 实现。
Introduction
超大规模的软件催生了按需分析的需求,比如按需指针分析。指针分析可以被划分为计算 points-to 信息的指向分析和计算别名信息的别名分析两种。文章认为,单独只进行一种指针分析是不能够满足下游分析的,比如污点分析。文章给出了一个例子:

如果只计算别名信息,下游分析可能需要多次查询来找到和 a 互为别名的所有变量。在传统工具中,污点分析常和指针分析深度耦合,这使得指针分析结果不能够重用。此外,如果不使用上下文敏感会导致结果不精确。理想情况下,污点分析应该能够在第 14 行针对特定的客户端定义的调用上下文对变量 a 发出唯一的指针查询,并直接获得 a 的所有可能别名。
Boomerang 是一种针对 Java 程序的需求驱动、流敏感、字段敏感和上下文敏感的指针分析。Boomerang 基于 IFDS 框架。
Background

首先介绍一下 IFDS 框架:
Dataflow domain
数据流域 \(D\) 由希望被推断出来的数据流事实组成,0 在上面的例子中指代始终成立的事实,是超图的 root。
Flow functions
流函数是是一个从 \(D\) 到 \(2^D\) 的映射。在过程间分析中,流函数可以被分为 normal 流函数,call 流函数,return 流函数和 call-to-return 流函数。IFDS 假设流函数满足可分配性,即 \(F(A \cup B) = F(A) \cup F(B)\)
Path edges
不同于流函数直接在超图上构建的边,路径边 \(\langle s, d_1 \rangle \rightarrow \langle t, d_2 \rangle\) 表示 \(s\) 位置的 \(d_1\) 成立时,\(t\) 位置的 \(d_2\) 也成立,表示一种总结。在函数的退出点结束的路径边可以被看作是某个函数的分析结果摘要。比如图中的红色虚线。
Access Graph
Boomerang 使用访问图来处理字段敏感性,实际上,访问图的域(记作 \(APG\))就是 boomerang 中数据流事实的域。记局部变量集合 \(L\),所有字段的集合 \(F\),用 \([l, s, E, t] \in L \times F \times 2^{F \times F} \times F\) 来表示一个字段图,变量 \(l\),首结点 \(s\),尾结点 \(t\),及对应的边集。一个访问图可能对应从首到尾的无穷多个访问路径。在访问图上有一些操作来捕获字段读写时访问图的变换,比如字段读取 \(x = y.f\) 就对应着从 \(y\) 的访问图中消耗掉首结点。
- Remove head
- Remove tail
- Prepend head
- Append tail
- Concatenate

Boomerang
Basic example
查询 \((s, \tilde{a})\) 表示在程序语句 \(s\) 上计算访问图 ̃\(\tilde{a}\) 的指针信息。

实线边代表反向分析,跟踪与变量 \(c\) 相关的所有 statement,直到一个分配点 \(L_2: b = new A()\) 为止。然后进行正向分析(虚线边),收集别名信息,找到能访问到分配点的访问图。正向路径边用于存储指向和别名信息。在这个例子中,\(L_4\) 语句上的正向路径边共享相同的起点,数据流事实可能别名,因此变量 \(b\) 和 \(c\) 可能别名,并且两者都在 \(L_2\) 处分配。
Handling field access
对于指针分析来说,流函数是 non-distributive 的。定义可能被别名信息影响的程序位置为 POI(point of indirection),Boomerang 使用一个外部不动点迭代算法(outer fixed-point iteration)来处理 POI:
例子:

算法:

\(L_4\) 是一个 POI,在处理 POI 的时候会产生一个子查询,查询 \(b\) 的所有可能别名,结果是集合 \(\{a, b\}\),因此一个新的正向路径边会被添加到超图中。
Flow functions
流函数接受一个语句和一个访问图作为输入,输出一组访问图,即 \(S \times \mathcal{APG} \rightarrow 2^{\mathcal{APG}}\)。反向分析被表示为 \([[s]]_b(\tilde{a})\)。
Backward analysis
使用 \(\tilde{a}_{=0}\) 表示访问图为空,\(\tilde{a}_{>0}\) 表示访问图非空。流函数对应的规则比较容易理解,其中类似 \([Alloc]\) 的标记表示这个 statement 是一个 POI,关于 POI 的处理会在后面描述:




Forward analysis
正向分析和反向分析的目的不同,反向分析是为了维护与查询的对象关联的每个访问图,正向分析是为了查找指向给定分配点的所有访问图。




Path edges
路径边是超图中方法内部任意节点之间的一个总结。\(\langle s, \tilde{a} \rangle \stackrel{f}\rightarrow \langle t, \tilde{b} \rangle\) 表示在语句 \(s\) 处通过访问图 \(\tilde{a}\) 可以访问的对象在语句 \(t\) 处通过访问图 \(\tilde{b}\) 也可以访问。一旦算法达到不动点,查询的结果将从所有的指向查询的正向路径边当中收集。
Implementation challenges

每个 POI 会有不同的处理方式:
- Allocation site:当反向分析发现一个分配点时,boomerang 会查找哪些访问图可能指向这个对象,因此一个正向的路径边 \(\langle a \leftarrow \text{new}, \tilde{a} \rangle \stackrel{f}\rightarrow \langle a \leftarrow \text{new}, \tilde{a} \rangle\) 会被添加到 forward solver 的 worklist 里面(启动一个正向分析)。
- Field write:当正向分析 $\langle r, \tilde{a} \rangle \stackrel{f}\rightarrow \langle t, \tilde{b} \rangle $ 遇到一个字段写入 \(x.f \leftarrow y\) 的时候,需要查询 \(x\) 的所有别名,即 \(Res(t,x)\) 。然后对所有 \(\tilde{\sigma} \in Res(t,x)\),取 \(\tilde{\gamma} = \tilde{\sigma} \cup \tilde{b}\),然后添加新的边 $\langle r, \tilde{a} \rangle \stackrel{f}\rightarrow \langle t, \tilde{\gamma} \rangle $,和之前的例子类似
- 其余的处理见论文
Evaluation
Updating...