基于soot的过程内数据流分析

原创 2016年05月30日 15:55:19

程序静态分析

程序静态分析(program static analysis)是指在不运行代码的方式下,通过词法分析、语法分析、控制流、数据流分析等技术对程序代码进行扫描,验证代码是否满足规范性、安全性、可靠性、可维护性等指标的一种代码分析技术。(百度百科


静态分析中的xxx-sensitive的一些理解:

(此内容来自知乎

xxx-sensitive是指静态分析中用于降低误报(false positive)的技术。存在flow-、path-、context-。

flow-insensitive:是把statements当作一个集合来看的,各个statement之间没有顺序,所以control flow statement(if、while)可以直接删除;flow-sensitive是说要关注statements之间的先后顺序。

path-insensitive:if、while静态分析时不知道动态的执行路线(path),所以一般会把它们不同的分支的数据流集merge起来。而path-sensitive则针对不同的路径的数据集不会merge,而是分别进行分析;

context-insensitve:只关心function之间的数据传递(参数、返回值、side-effect)而忽略了同一函数在不同call side下不同的context,即忽略了call stack。将call site和return当作goto,并添加一些赋值语句,这样造成的情况是,第一个call site处正常,第二个call site时返回值可能出现两个。

 

静态分析时,无论是分析源代码还是目标代码,分析的对象(方法、语句、变量)都只有一份:同一个方法我们只会写一个方法体(方法体里的语句也就只有一份)。同一个变量只会声明一次。然后动态运行程序的时候:

一个方法可能会被调用N次,每次调用的上下文可以不一样,不同上下文中这个方法里的变量的值会不同;一个方法里,一个变量的不同位置的值也会不一样;一个方法里同一个位置的变量的值在程序执行不同路径时也不一样。写的方法、语句、变量在动态运行时仿佛有了“分身”,每个分身都有自己的值。静态分析的时候对于同一个对象只能看到一个实体,如果直接分析,一个变量所有“分身”的相关属性会全部合并,并且一个变量的属性合并了,会影响其他变量的分析结果。

静态分析为得到准确的结果,就得为分析的对象模拟动态运行时的分身。

xxx-sensitive就是在静态分析时,按照xxx给程序里的对象(模拟动态运行)创建“分身”(或者说按照xxx区分分析对象):按照上下文区分叫作context-sensitive;按照位置区分叫作flow-sensitive;按照路径区分叫作path-sensitive。区分之后就可以减少false positive。

数据流分析

(此内容来自龙书)

数据流分析:指的是一组用来获取有关数据如何沿着程序执行路径(control-flow graph)流动的相关信息的技术。

 

在所有的数据流分析应用中,我们都会把每个程序点和一个数据流(data-flow value)关联起来。这个值是在该点可能观察到的所有程序状态的集合的抽象表示。所有可能的数据流值的集合称为这个数据流应用的域(domain)。

 

我们把每个语句s之前和之后的数据流值分别记为IN[s]和OUT[s]。数据流问题(data-flow problem)就是要对一组约束求解。这组约束对所有的语句s限定了IN[s]和OUT[s]之间的关系。约束分为两种:基于语句语义(传递函数)的约束和基于控制流的约束。

 

数据流分析一般分为:intra-procedural analysis和inter-proceduralanalysis,在上篇Phase中提到,jtp pack和wjtp pack可以分别被用来实现自定义的数据流分析

 

实现数据流分析前需要搞清楚的问题

数据流的方向(前向、后向);交汇运算采用交集还是并集(当path-insenstitive时);传递函数;数据流集合的初始化。

采用soot框架实现过程内数据流分析

(此部分内容参考点击打开链接

1.      过程内数据流分析

过程内数据流分析(intra-proceduraldata-flow analysis)指在一个单独方法的控制流图上操作。在soot中的控制流图为UnitGraph。UnitGraph中节点表示statements,如果在控制流上一个表示source node流向target node,那么这两个结点存在边(edge)。

数据流分析都与unitgraph每个节点上的两个元素有关,这两个集合被称为:in-set和out-set。这些集合会被初始化,然后沿着语句节点传播,指导一个定点抵达才停止。

最后,你要做的就是检查每个句子的前后的flow set。通过设计的数据流分析,你的flow sets应该会直接告诉你所需要的信息。

 

2.      Forward、backward orbranched(解决数据流的方向问题)?

在soot中FlowAnalysis存在三种不同类型的方法:

ForwardFlowAnalysis:这个分析以UnitGraph的entrystatement作为开始并开始传播;

BackwardsFlowAnalysis:这个分析以UnitGraph的exit node(s)作为分析并且向后开始传播(当然可以将UnitGraph转换产生inverseGraph,然后再使用ForwardFlowAnalysis进行分析);

ForwardBranchedFlowAnalysis:这个分析本质上也是Forward分析,但是它允许你在不同分支处传递不同的flow sets。例如:如果传播到如if(p!=null)语句处,当“p is not null”时,传播进入“then”分支,当“p is null”时传播进入“else”分支(Forward、backward分析都在分支处会将分析结果merge)。

 

3.      实现过程间数据流分析的关键方法

Constructor

必须实现一个携带DirectedGraph作为参数的构造函数,并且将该参数传递给super constructor。然后,在构造函数结束时调用doAnalysis(),doAnalysis()将真正执行数据流分析。而在调用super constructor和doAnalysis之间,可以自定义数据分析结构。

<span style="font-size:14px;">public MyAnalysis(DirectedGraph graph) { //构造函数
	super(graph);
	// TODO Auto-generated constructor stub
	emptySet = new ArraySparseSet();
	doAnalysis();//执行fixed-point
}</span>


newInitialFlow()和entryInitialFlow()(数据流集合的初始化问题

newInitialFlow()方法返回一个对象,这个对象被赋值给每个语句的in-set和out-set集合,除过UnitGraph的第一个句子的in-set集合(如果你实现的是backwards分析,则是一个exit statement语句)。第一个句子的in-set集合由entryInitialFlow()初始化。

<span style="font-size:14px;">@Override
protected Object newInitialFlow() {
	// TODO Auto-generated method stub
	return emptySet.emptySet();
}

@Override
protected Object entryInitialFlow() {
	// TODO Auto-generated method stub
	return emptySet.emptySet();
}</span>

copy(..)

copy(..)方法携带两个参数,一个source和一个target。它仅仅实现将source中的元素拷贝到target中。

<span style="font-size:14px;">@Override
protected void copy(Object source, Object dest) {
	// TODO Auto-generated method stub
	FlowSet srcSet = (FlowSet)source,
	destSet = (FlowSet)dest;
	srcSet.copy(destSet);
}</span>


merge(..)(数据流的交汇运算问题

merge(..)方法被用来在control-flow的合并点处合并数据流集,例如:在句子(if/then/else)分支的结束点。与copy(..)不同的是,它携带了三个参数,一个参数是来自左边分支的out-set,一个参数是来自右边分支的out-set,另外一个参数是两个参数merge后的集合,这个集合将是合并点的下一个句子的in-set集合。

注:merge(..)本质上指的是控制流的交汇运算,一般根据待分析的具体问题来决定采用并集还是交集。

<span style="font-size:14px;">@Override
protected void merge(Object in1, Object in2, Object out) {
	// TODO Auto-generated method stub
	FlowSet inSet1 = (FlowSet)in1,
	inSet2 = (FlowSet)in2,
	outSet = (FlowSet)out;
	//inSet1.union(inSet2, outSet);
	inSet1.intersection(inSet2, outSet);
}</span>

flowThrough(..)(数据流的传递函数问题

flowThrough(..)方法是真正执行流函数,它有三个参数:in-set、被处理的节点(一般指的就是句子Unit)、out-set。这个方法的实现内容完全取决于你的分析。

注:flowThrough()本质上就是一个传递函数。在一个语句之前和之后的数据流值受该语句的语义的约束。比如,假设我们的数据流分析涉及确定各个程序点上各变量的常量值。如果变量a在执行语句b=a之前的值为v,那么在该语句之后a和b的值都是v。一个赋值语句之前和之后的数据流值的关系被称为传递函数。针对前向分析和后向分析,传递函数有两种风格。

<span style="font-size:14px;">@Override
protected void flowThrough(Object in, Object d, Object out) {
	// TODO Auto-generated method stub
	FlowSet inSet = (FlowSet)in,
	outSet = (FlowSet)out;
	Unit u = (Unit) d;
	kill(inSet,u,outSet);
	gen(outSet,u);
}

private void kill(FlowSet inSet, Unit u, FlowSet outSet) {
	// TODO Auto-generated method stub
	FlowSet kills = (FlowSet)emptySet.clone();//Unit的kills
	Iterator defIt = u.getDefBoxes().iterator();
	while(defIt.hasNext()){
		ValueBox defBox = (ValueBox)defIt.next();
			
		if(defBox.getValue() instanceof Local){
			Iterator inIt = inSet.iterator();
			while(inIt.hasNext()){
				Local inValue = (Local)inIt.next();
				if(inValue.equivTo(defBox.getValue())){
					kills.add(defBox.getValue());
			}
		}
	<span style="white-space:pre">	</span>}
	}
	inSet.difference(kills, outSet);
}

private void gen(FlowSet outSet, Unit u) {
	// TODO Auto-generated method stub
	Iterator useIt = u.getUseBoxes().iterator();
	while(useIt.hasNext()){
		ValueBox e = (ValueBox)useIt.next();
		if(e.getValue() instanceof Local)
			outSet.add(e.getValue());
	}
}</span>


Flow sets

(此部分内容参考点击打开链接

    在soot中,flow sets代表control-flowgraph中与节点相关的数据集合。Flow set存在有界限的(interface BoundedFlowSet)和无界限的(interfaceFlowSet)两种表达。有界限的集合知道可能值的全体集合,而无界限的集合则不知道。

Interface FlowSet<T>提供的关键方法有

<span style="font-size:14px;">FlowSet<T> clone() //克隆当前FlowSet的集合
FlowSet<T> emptySet() //返回一个空集,通常比((FlowSet)clone()).clear()效率更高
void copy(FlowSet<T> dest) //拷贝当前集合到dest集合中
void union(FlowSet<T> other) //FlowSet∪other = FlowSet
void union(FlowSet<T> other,FlowSet<T> dest) // FlowSet∪other = dest,其中other、dest可以与该FlowSet一样
void intersection(FlowSet<T> other) //FlowSet∩other = FlowSet
void intersection(FlowSet<T> other,FlowSet<T> dest) // FlowSet∩other = FlowSet,其中,dest、other可以和该FlowSet一样
void difference(FlowSet<T> other) // FlowSet-other = FlowSet
void difference(FlowSet<T> other,FlowSet<T> dest) // FlowSet-other = dest,其中,dest、other和FlowSet可能相同。
</span>

还有isEmpty()、size()、add(T obj)、remove(T obj)、contains(Tobj)、isSubSet(FlowSet<T> other)、iterator()、toList()等。

上述方法足以使flow sets成为一个有效的lattice元素。


当实现BoundedFlowSet时,它需要提供方法,该方法能够产生set‘s complement和its topped set(一个lattice element包括所有的可能的值的集合)。

Soot提供了四种flow sets的实现:ArraySparseSet,ArrayPackedSet,ToppedSet和DavaFlowSet。

ArraySparseSet:是一个无界限的flowset。该set代表一个数组引用。注意:当比较元素是否相等时,一般使用继承自Object对象的equals。但是在soot中的元素都是代表一些代码结构,不能覆写equals方法。而是实现了interface soot.EquivTo。因此,如果你需要一个包含类似binary operation expressions的集合,你需要使用equivTo方法实现自定义的比较方法去比较是否相等


针对intra-procedural analysis,本人实现了一个活跃变量的代码

import java.util.Iterator;

import soot.Local;
import soot.Unit;
import soot.ValueBox;
import soot.toolkits.graph.DirectedGraph;
import soot.toolkits.scalar.ArraySparseSet;
import soot.toolkits.scalar.BackwardFlowAnalysis;
import soot.toolkits.scalar.FlowSet;

class MyAnalysis extends BackwardFlowAnalysis{
	
	private FlowSet emptySet;

	public MyAnalysis(DirectedGraph graph) { //构造函数
		super(graph);
		// TODO Auto-generated constructor stub
		emptySet = new ArraySparseSet();
		doAnalysis();//执行fixed-point
	}

	@Override
	protected void flowThrough(Object in, Object d, Object out) {
		// TODO Auto-generated method stub
		FlowSet inSet = (FlowSet)in,
				outSet = (FlowSet)out;
		Unit u = (Unit) d;
		kill(inSet,u,outSet);
		gen(outSet,u);
	}

	private void kill(FlowSet inSet, Unit u, FlowSet outSet) {
		// TODO Auto-generated method stub
		FlowSet kills = (FlowSet)emptySet.clone();//Unit的kills
		Iterator defIt = u.getDefBoxes().iterator();
		while(defIt.hasNext()){
			ValueBox defBox = (ValueBox)defIt.next();
			
			if(defBox.getValue() instanceof Local){
				Iterator inIt = inSet.iterator();
				while(inIt.hasNext()){
					Local inValue = (Local)inIt.next();
					if(inValue.equivTo(defBox.getValue())){
						kills.add(defBox.getValue());
					}
				}
			}
		}
		inSet.difference(kills, outSet);
	}

	private void gen(FlowSet outSet, Unit u) {
		// TODO Auto-generated method stub
		Iterator useIt = u.getUseBoxes().iterator();
		while(useIt.hasNext()){
			ValueBox e = (ValueBox)useIt.next();
				if(e.getValue() instanceof Local)
					outSet.add(e.getValue());
			}
		}


	@Override
	protected Object newInitialFlow() {
		// TODO Auto-generated method stub
		return emptySet.emptySet();
	}

	@Override
	protected Object entryInitialFlow() {
		// TODO Auto-generated method stub
		return emptySet.emptySet();
	}

	@Override
	protected void merge(Object in1, Object in2, Object out) {
		// TODO Auto-generated method stub
		FlowSet inSet1 = (FlowSet)in1,
				inSet2 = (FlowSet)in2,
				outSet = (FlowSet)out;
		//inSet1.union(inSet2, outSet);
		inSet1.intersection(inSet2, outSet);
	}

	@Override
	protected void copy(Object source, Object dest) {
		// TODO Auto-generated method stub
		FlowSet srcSet = (FlowSet)source,
				destSet = (FlowSet)dest;
		srcSet.copy(destSet);
	}

}




版权声明:本文为博主原创文章,未经博主允许不得转载。

指针分析/Point-to Analysis/Reference Analysis

目录目录 指针分析 指针分析的目的 基础 Anderson 算法 例子 图示 指针分析指针分析紧接数据流分析,是静态分析中的一个难点。遗憾的是能把相关内容讲得通俗易懂的资料非常少,中文的更基本没有。所...

Soot数据流 -- 数据流框架

1.数据流分析框架关键步骤。 个人的问题:  什么叫做数据流分析?为何将下列问题作为关键步骤? 关键问题及步骤(soot生存手册中提到): 1.1. 决定分析的本质是什么:是否向前后者向后的数据...

Soot II: 数据流框架

设计数据流分析包含4步 1. 决定分析的本质。是向前还是向后?是否需要对分支特殊考虑? 2. 决定近似的目标。是may还是must? 具体来说是在merge时找交集还是并集。 3....

Soot 学习笔记 3:实现过程内的数据流分析

过程内数据流分析(intra-procedural data-flow analysis)是针对某一个方法的控制流图(也就是 soot 中的 UnitGraph )操作的。UnitGraph 是以 s...

数据流分析(一)

引子编译器后端会对前端生成的中间代码做很多优化,也就是在保证程序语义不变的前提下,提高程序执行的效率或减少代码size等优化目标。优化需要依靠代码分析给出的“指导信息”来相应地改进代码,而代码分析中最...

数据流分析(二)

引子我们在数据流分析(一)中简要介绍了数据流分析的基本概念,下面我们集中分析一些数据流分析的实例来阐述数据流分析的核心思想。 到达定值 活变量 可用表达式 到达定值什么是到达定值“到达定值”是最常见的...

Soot I: 基本了解

Static analysis指在不对program进行运行的情况下,对其行为进行分析。搞compiler的人用之于优化,搞安全的用于做taint analysis。对于Java有两大开源的stati...

soot基础 -- soot 中的处理阶段

1.概述 soot的执行被划分成了很多阶段,这些阶段的名字叫做packs. soot的设计值得学习,通过划分阶段,将每个阶段处理的职责分清楚。 同时,也正确的处理了各个阶段的依赖关系。 2.soot...

Soot中执行过程内数据流分析

原文链接:https://github.com/Sable/soot/wiki/Implementing-an-intra-procedural-data-flow-analysis-in-Soot ...

Soot学习笔记(2)

通过Soot来分析一个java bytecode(即class file),首先我先参考了一下下面这篇博文 http://www.cnblogs.com/quyu/archive/2012/01/04...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:基于soot的过程内数据流分析
举报原因:
原因补充:

(最多只允许输入30个字)