静态程序分析之数据流分析(Foundations+LiveVarAnalysisCode)续

admin 2025-12-14 22:59:30 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 文章介绍了静态程序分析中的数据流分析技术,包括证明转换函数单调性的方法、格的高度概念、May和Must分析的区别、常量传播分析以及Worklist算法优化。文章最后提供了基于Tai-e框架的LiveVariableAnalysis的完整代码实现,包括三个关键类的详细代码,展示了如何在实际框架中实现数据流分析算法。 综合评分: 85 文章分类: 代码审计,安全工具,技术标准,二进制安全,其他


cover_image

静态程序分析之数据流分析(Foundations + LiveVar Analysis Code)续

TeddyBe4r

看雪学苑

2025年10月22日 20:57 上海

从Bottom出发达到的一定是最小不动点,从Top出发到达的一定是最大不动点。

Prove Function is Monotonic(证明转换函数单调性)

对于Transfer Function的单调性,我们需要回到之前的章节

以这个为例子,在课程中笔者认为该例子过于晦涩难懂,仅仅因为 genB固定 KillB固定就证明该函数是单调的。但实际上我们可以通过一个不严谨的思想来理解这个Transfer Function是单调的,我们考虑将该函数类比为 普通的一次函数。

y = const1 + x - const2

其中

y = OUT[B]

const1 = genb

x = IN[B]

const2 = killB

通过如上的类似仿射变换的思想可以帮助我们理解Transfer Funcion的单调性。

合理严格的证明如下

这里的证明思路还是阐述一下方便理解。

1.首先我们根据lub 的定义可以得出y <= y V z因为y V z是 y的lub

2.根据传递性 因为x <= yy <= y V z所以x <= y V z

3.然后我们又一次根据 lub的定义x V z是x的lub 而y V z是  x的ub

4.根据lub的定义 lub 必定小于等于 ub

5.所以我们能够得出x V z <= y V z

经过如上的证明我们能够将数据分析的问题转化到格上,且能够通过获取lfp和gfp 得到Best Fixed Point,也能够证明这些问题是一定有解的。

现在我们需要讨论什么时候我们能够抵达不动点。为了解决这个问题我们需要引入格的高度这个概念。

Height of Lattice(格的高度)

#

格的高度就是从 Top 到 Buttom最长的一条路径就是 格的高度

最坏的迭代次数就是格高度 * 节点个数该次数是悲观上界 辅佐以下图理解,不过我们需要申明几个前提

1.每次走一步

2.每次只有一个节点变化

CFG节点 B1: &nbsp;OUT[B1] = ∅ → {a} → {a,b} → {a,b,c}
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ↑ &nbsp; 格中的某个元素
CFG节点 B2: &nbsp;OUT[B2] = ∅ → {c} → {b,c} → {a,b,c}
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ↑ &nbsp; 格中的某个元素
CFG节点 B3: &nbsp;OUT[B3] = ∅ → {b} → {a,b} → {a,b,c}
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ↑ &nbsp; 格中的某个元素
May And Must Analysis, a Lattice view(从格视角理解May 和 Must 分析)

#

我们可以考虑把一个PL的 抽象的Lattice 的 Product Lattice看成一个新的Lattice

#

May Analysis

#

对于May 我们通常是从 Bottom 出发从一个unsafe result到 safe result的过程

结合这张图我们用 Reach Def 举例子

在Reaching Definitions问题中,我们在Entry块给所有变量一个undefined定义。在格中:

Bottom (⊥): 空集,表示没有信息,这是分析的起点(信息最不完整)

Top (⊤): 全集,表示所有定值(包括所有undefined)都可能到达,这是最保守的过近似(最不精确)

迭代过程: 从Bottom出发,单调地向上增长,逐步添加可达的定值

不动点: 最终收敛到最小不动点,这是最精确的安全解

语义: 如果某点的undefined定义可达,说明变量可能未初始化(不安全);如果不可达,说明变量已被定义(安全)

这里我们做一个类比理解

想象你问:”明天会下雨吗?”

| | | | | — | — | — | | 回答 | 对应 | 评价 | | “不知道”(啥都没说) | ⊥ (Bottom) | ❌ 不靠谱(可能错过重要信息) | | “有30%概率下雨” | 不动点 | ✅ 靠谱且有用 | | “明天可能下雨,可能晴天,可能下雪,可能刮风,可能地震,可能外星人入侵…” | ⊤ (Top) | ✅ 靠谱(肯定不会错)❌ 但完全没用 |

Must Analysis

Available Expressions分析是Must分析,目标是找出在某个程序点肯定被计算过且还有效的表达式(用于优化,如公共子表达式消除)。

格的结构

Top (⊤): 所有可能表达式的全集

Bottom (⊥): 空集 ∅(没有表达式available)

偏序关系: 集合包含 ⊇(注意方向相反!)

合并操作: 交集 ∩

Top (⊤) = 所有表达式的全集

◆含义:假设所有表达式都available

◆这是Unsafe的(过度乐观,可能错误)

◆如果用这个结果做优化可能出错

Bottom (⊥) = ∅ (空集)

◆含义:没有表达式available

◆这是Safe的(最保守,绝对不会出错)

◆但最没用(无法做任何优化)

我们给出该图进行回顾总结

Definitions问题

| | | | | — | — | — | | 视角 | 问题 | 分析类型 | | May | “哪些定值可能到达?” | May Reaching Defs | | Must | “哪些定值一定到达?” | Must Reaching Defs |

Expressions问题

| | | | | — | — | — | | 视角 | 问题 | 分析类型 | | May | “哪些表达式可能被计算过?” | May Available Expr | | Must | “哪些表达式一定被计算过?” | Must Available Expr |

#

How Precise is Our Solution?

◆Meet Over-All-Paths Solution(MOP)

MOP和常见的迭代算法的结果区别

一个是 利用了控制流的Merge一个是用最后的节点的路径去做Merge

Mop 和 迭代算法精度的区别

当算法具有可分配性的时候可以视作 MOP和 Ours的精确度相等。对于Gen/Kill 问题都是具有可分配性的

Constant Propagetion(常量传播)

Data Abstruct And Transfer Function

undefine在L里是top,其他常量≤undef,若undef的一个函数输出是常量,则这个常量可能小于其他常量的函数的输出结果,导致gen(undef)≤gen(c) 不满足单调性

Worklist Algorithm(Iterator 的优化算法)

以下就是Worklist

对于Worklist算法其实就是对 迭代算法的一种优化,只操作有变化的集合。

接下来我给出基于Tai-E框架的对Live Var Analysis 的 代码。

/* LiveVariableAnalysis.java */

/*
&nbsp;* Tai-e: A Static Analysis Framework for Java
&nbsp;*
&nbsp;* Copyright (C) 2022 Tian Tan <[email protected]>
&nbsp;* Copyright (C) 2022 Yue Li <[email protected]>
&nbsp;*
&nbsp;* This file is part of Tai-e.
&nbsp;*
&nbsp;* Tai-e is free software: you can redistribute it and/or modify
&nbsp;* it under the terms of the GNU Lesser General Public License
&nbsp;* as published by the Free Software Foundation, either version 3
&nbsp;* of the License, or (at your option) any later version.
&nbsp;*
&nbsp;* Tai-e is distributed in the hope that it will be useful,but WITHOUT
&nbsp;* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
&nbsp;* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
&nbsp;* Public License for more details.
&nbsp;*
&nbsp;* You should have received a copy of the GNU Lesser General Public
&nbsp;* License along with Tai-e. If not, see <https://www.gnu.org/licenses/>.
&nbsp;*/

package pascal.taie.analysis.dataflow.analysis;

import&nbsp;pascal.taie.analysis.dataflow.fact.SetFact;
import&nbsp;pascal.taie.analysis.graph.cfg.CFG;
import&nbsp;pascal.taie.config.AnalysisConfig;
import&nbsp;pascal.taie.ir.exp.RValue;
import&nbsp;pascal.taie.ir.exp.Var;
import&nbsp;pascal.taie.ir.stmt.Stmt;

/**
&nbsp;* Implementation of classic live variable analysis.
&nbsp;*/
public&nbsp;class&nbsp;LiveVariableAnalysis&nbsp;extends
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;AbstractDataflowAnalysis<Stmt,&nbsp;SetFact<Var>> {

&nbsp; &nbsp;&nbsp;public&nbsp;static&nbsp;final&nbsp;String&nbsp;ID&nbsp;=&nbsp;"livevar";

&nbsp; &nbsp;&nbsp;public&nbsp;LiveVariableAnalysis(AnalysisConfig&nbsp;config) {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;super(config);
&nbsp; &nbsp; }

&nbsp; &nbsp;&nbsp;@Override
&nbsp; &nbsp;&nbsp;public&nbsp;boolean&nbsp;isForward() {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;false;
&nbsp; &nbsp; }

&nbsp; &nbsp;&nbsp;@Override
&nbsp; &nbsp;&nbsp;public&nbsp;SetFact<Var>&nbsp;newBoundaryFact(CFG<Stmt> cfg) {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// TODO - finish me
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;System.out.println("[*] bottom init");

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;new&nbsp;SetFact<>();
&nbsp; &nbsp; }

&nbsp; &nbsp;&nbsp;@Override
&nbsp; &nbsp;&nbsp;public&nbsp;SetFact<Var>&nbsp;newInitialFact() {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// TODO - finish me
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// System.out.println("[*] assume all pl without live var");
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;new&nbsp;SetFact<>();
&nbsp; &nbsp; }

&nbsp; &nbsp;&nbsp;@Override
&nbsp; &nbsp;&nbsp;public&nbsp;void&nbsp;meetInto(SetFact<Var> fact, SetFact<Var> target) {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// TODO - finish me
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// System.out.println("[*] meet");
&nbsp; &nbsp; &nbsp; &nbsp; target.union(fact);
&nbsp; &nbsp; }

&nbsp; &nbsp;&nbsp;@Override
&nbsp; &nbsp;&nbsp;public&nbsp;boolean&nbsp;transferNode(Stmt stmt, SetFact<Var>&nbsp;in, SetFact<Var> out) {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// TODO - finish me
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 保存 in 的旧值,用于比较是否发生变化
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;SetFact<Var> oldIn =&nbsp;in.copy();

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 1. 先将 OUT[s] 复制到 IN[s]
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;in.set(out);

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 2. 处理定义(def):移除被定义的变量
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 获取语句中被定义的变量(赋值语句的左侧)
&nbsp; &nbsp; &nbsp; &nbsp; stmt.getDef().ifPresent(lValue -> {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(lValue&nbsp;instanceof&nbsp;Var) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 从 IN 中移除被定义的变量
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;in.remove((Var) lValue);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; });

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 3. 处理使用(use):添加被使用的变量
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 获取语句中使用的所有变量(赋值语句的右侧)
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;(RValue&nbsp;rValue : stmt.getUses()) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(rValue&nbsp;instanceof&nbsp;Var) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 将使用的变量添加到 IN 中
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;in.add((Var) rValue);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; }

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 4. 检查 IN 是否发生变化
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 如果改变了,迭代求解器需要继续迭代
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;!in.equals(oldIn);
&nbsp; &nbsp; }
}

/* IterativeSolver.java */
/*
&nbsp;* Tai-e: A Static Analysis Framework for Java
&nbsp;*
&nbsp;* Copyright (C) 2022 Tian Tan <[email protected]>
&nbsp;* Copyright (C) 2022 Yue Li <[email protected]>
&nbsp;*
&nbsp;* This file is part of Tai-e.
&nbsp;*
&nbsp;* Tai-e is free software: you can redistribute it and/or modify
&nbsp;* it under the terms of the GNU Lesser General Public License
&nbsp;* as published by the Free Software Foundation, either version 3
&nbsp;* of the License, or (at your option) any later version.
&nbsp;*
&nbsp;* Tai-e is distributed in the hope that it will be useful,but WITHOUT
&nbsp;* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
&nbsp;* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
&nbsp;* Public License for more details.
&nbsp;*
&nbsp;* You should have received a copy of the GNU Lesser General Public
&nbsp;* License along with Tai-e. If not, see <https://www.gnu.org/licenses/>.
&nbsp;*/

package pascal.taie.analysis.dataflow.solver;

import&nbsp;pascal.taie.analysis.dataflow.analysis.DataflowAnalysis;
import&nbsp;pascal.taie.analysis.dataflow.fact.DataflowResult;
import&nbsp;pascal.taie.analysis.graph.cfg.CFG;

class&nbsp;IterativeSolver<Node,&nbsp;Fact>&nbsp;extends&nbsp;Solver<Node,&nbsp;Fact> {

&nbsp; &nbsp;&nbsp;public&nbsp;IterativeSolver(DataflowAnalysis<Node,&nbsp;Fact> analysis) {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;super(analysis);
&nbsp; &nbsp; }

&nbsp; &nbsp;&nbsp;@Override
&nbsp; &nbsp;&nbsp;protected&nbsp;void&nbsp;doSolveForward(CFG<Node> cfg, DataflowResult<Node, Fact> result) {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;throw&nbsp;new&nbsp;UnsupportedOperationException();
&nbsp; &nbsp; }

&nbsp; &nbsp;&nbsp;@Override
&nbsp; &nbsp;&nbsp;protected&nbsp;void&nbsp;doSolveBackward(CFG<Node> cfg, DataflowResult<Node, Fact> result) {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// TODO - finish me
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 注意:不要在这里调用 initializeBackward
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 初始化已经在 Solver.solve() -> initialize() 中完成了

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;boolean&nbsp;changed =&nbsp;true; &nbsp;// 标记是否有变化

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 不动点迭代:重复直到没有变化
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;while&nbsp;(changed) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; changed =&nbsp;false; &nbsp;// 假设本轮没有变化

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 遍历 CFG 中的所有节点(除了 EXIT)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 对于后向分析,我们从 EXIT 开始向前传播
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;(Node&nbsp;node : cfg) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 跳过 EXIT 节点(它的 OUT 是边界条件,不会改变)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(node == cfg.getExit()) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;continue;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// ========== Step 1: Meet 操作 ==========
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// OUT[B] = ⋃ IN[S] (对所有后继 S)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 对于活跃变量分析:OUT[B] = IN[S1] ∪ IN[S2] ∪ ...

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 获取当前节点的所有后继
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 后继的 IN 会汇聚到当前节点的 OUT
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;(Node&nbsp;succ : cfg.getSuccsOf(node)) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 调用 meetInto 将后继的 IN 合并到当前节点的 OUT
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// meetInto(fact, target): 将 fact 合并到 target
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; analysis.meetInto(
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; result.getInFact(succ), &nbsp;&nbsp;// 后继的 IN fact
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; result.getOutFact(node) &nbsp;&nbsp;// 当前节点的 OUT fact(会被修改)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; );
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// ========== Step 2: Transfer 函数 ==========
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// IN[B] = transfer(B, OUT[B])
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 对于活跃变量分析:IN[B] = use[B] ∪ (OUT[B] - def[B])

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// transferNode 会:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 1. 根据 OUT[B] 计算新的 IN[B]
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 2. 如果 IN[B] 发生改变,返回 true
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(analysis.transferNode(
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; node, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// 当前节点
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; result.getInFact(node), &nbsp; &nbsp;// IN fact(会被修改)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; result.getOutFact(node) &nbsp; &nbsp;// OUT fact(只读)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; )) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 如果 IN 发生了变化,需要继续迭代
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; changed =&nbsp;true;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; }

&nbsp; &nbsp; }
}

/* Solver.java */
/*
&nbsp;* Tai-e: A Static Analysis Framework for Java
&nbsp;*
&nbsp;* Copyright (C) 2022 Tian Tan <[email protected]>
&nbsp;* Copyright (C) 2022 Yue Li <[email protected]>
&nbsp;*
&nbsp;* This file is part of Tai-e.
&nbsp;*
&nbsp;* Tai-e is free software: you can redistribute it and/or modify
&nbsp;* it under the terms of the GNU Lesser General Public License
&nbsp;* as published by the Free Software Foundation, either version 3
&nbsp;* of the License, or (at your option) any later version.
&nbsp;*
&nbsp;* Tai-e is distributed in the hope that it will be useful,but WITHOUT
&nbsp;* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
&nbsp;* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
&nbsp;* Public License for more details.
&nbsp;*
&nbsp;* You should have received a copy of the GNU Lesser General Public
&nbsp;* License along with Tai-e. If not, see <https://www.gnu.org/licenses/>.
&nbsp;*/

package pascal.taie.analysis.dataflow.solver;

import&nbsp;pascal.taie.analysis.dataflow.analysis.DataflowAnalysis;
import&nbsp;pascal.taie.analysis.dataflow.fact.DataflowResult;
import&nbsp;pascal.taie.analysis.graph.cfg.CFG;

/**
&nbsp;* Base class for data-flow analysis solver, which provides common
&nbsp;* functionalities for different solver implementations.
&nbsp;*
&nbsp;*&nbsp;@param&nbsp;<Node> type of CFG nodes
&nbsp;*&nbsp;@param&nbsp;<Fact> type of data-flow facts
&nbsp;*/
public&nbsp;abstract&nbsp;class&nbsp;Solver<Node,&nbsp;Fact> {

&nbsp; &nbsp;&nbsp;protected&nbsp;final&nbsp;DataflowAnalysis<Node,&nbsp;Fact> analysis;

&nbsp; &nbsp;&nbsp;protected&nbsp;Solver(DataflowAnalysis<Node,&nbsp;Fact> analysis) {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;this.analysis&nbsp;= analysis;
&nbsp; &nbsp; }

&nbsp; &nbsp;&nbsp;/**
&nbsp; &nbsp; &nbsp;* Static factory method to create a new solver for given analysis.
&nbsp; &nbsp; &nbsp;*/
&nbsp; &nbsp;&nbsp;public&nbsp;static&nbsp;<Node,&nbsp;Fact>&nbsp;Solver<Node,&nbsp;Fact>&nbsp;makeSolver(
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; DataflowAnalysis<Node, Fact> analysis) {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;new&nbsp;IterativeSolver<>(analysis);
&nbsp; &nbsp; }

&nbsp; &nbsp;&nbsp;/**
&nbsp; &nbsp; &nbsp;* Starts this solver on the given CFG.
&nbsp; &nbsp; &nbsp;*
&nbsp; &nbsp; &nbsp;*&nbsp;@param&nbsp;cfg control-flow graph where the analysis is performed on
&nbsp; &nbsp; &nbsp;*&nbsp;@return&nbsp;the analysis result
&nbsp; &nbsp; &nbsp;*/
&nbsp; &nbsp;&nbsp;public&nbsp;DataflowResult<Node,&nbsp;Fact>&nbsp;solve(CFG<Node> cfg) {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;DataflowResult<Node,&nbsp;Fact> result =&nbsp;initialize(cfg);
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;doSolve(cfg, result);
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;result;
&nbsp; &nbsp; }

&nbsp; &nbsp;&nbsp;/**
&nbsp; &nbsp; &nbsp;* Creates and initializes a new data-flow result for given CFG.
&nbsp; &nbsp; &nbsp;*
&nbsp; &nbsp; &nbsp;*&nbsp;@return&nbsp;the initialized data-flow result
&nbsp; &nbsp; &nbsp;*/
&nbsp; &nbsp;&nbsp;private&nbsp;DataflowResult<Node,&nbsp;Fact>&nbsp;initialize(CFG<Node> cfg) {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;DataflowResult<Node,&nbsp;Fact> result =&nbsp;new&nbsp;DataflowResult<>();
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(analysis.isForward()) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;initializeForward(cfg, result);
&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp;else&nbsp;{
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;initializeBackward(cfg, result);
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;result;
&nbsp; &nbsp; }

&nbsp; &nbsp;&nbsp;protected&nbsp;void&nbsp;initializeForward(CFG<Node> cfg, DataflowResult<Node, Fact> result) {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;throw&nbsp;new&nbsp;UnsupportedOperationException();
&nbsp; &nbsp; }

&nbsp; &nbsp;&nbsp;protected&nbsp;void&nbsp;initializeBackward(CFG<Node> cfg, DataflowResult<Node, Fact> result) {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// TODO - finish me
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 1. 为 EXIT 节点设置边界条件
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// EXIT 是后向分析的起点(边界)
&nbsp; &nbsp; &nbsp; &nbsp; result.setOutFact(cfg.getExit(), analysis.newBoundaryFact(cfg));

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 2. 为 CFG 中的所有节点设置初始 fact
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;(Node&nbsp;node : cfg) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 初始化所有节点的 IN fact(防止 null)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; result.setInFact(node, analysis.newInitialFact());

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 初始化非 EXIT 节点的 OUT fact
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(node != cfg.getExit()) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; result.setOutFact(node, analysis.newInitialFact());
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }

&nbsp; &nbsp;&nbsp;/**
&nbsp; &nbsp; &nbsp;* Solves the data-flow problem for given CFG.
&nbsp; &nbsp; &nbsp;*/
&nbsp; &nbsp;&nbsp;private&nbsp;void&nbsp;doSolve(CFG<Node> cfg, DataflowResult<Node, Fact> result) {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(analysis.isForward()) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;doSolveForward(cfg, result);
&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp;else&nbsp;{
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;doSolveBackward(cfg, result);
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }

&nbsp; &nbsp;&nbsp;protected&nbsp;abstract&nbsp;void&nbsp;doSolveForward(CFG<Node> cfg,&nbsp;DataflowResult<Node,&nbsp;Fact> result);

&nbsp; &nbsp;&nbsp;protected&nbsp;abstract&nbsp;void&nbsp;doSolveBackward(CFG<Node> cfg,&nbsp;DataflowResult<Node,&nbsp;Fact> result);
}

看雪ID:TeddyBe4r

https://bbs.kanxue.com/user-home-983513.htm

*本文为看雪论坛精华文章,由 TeddyBe4r 原创,转载请注明来自看雪社区

倒计时!看雪·第九届安全开发者峰会(SDC2025)

往期推荐

无”痕”加载驱动模块之傀儡驱动 (上)

为 CobaltStrike 增加 SMTP Beacon

隐蔽通讯常见种类介绍

buuctf-re之CTF分析

物理读写/无附加读写实验

球分享

球点赞

球在看

点击阅读原文查看更多


评论:0   参与:  3