【SVF-1】SVF过程间静态数值流分析简介

一、SVF介绍

参考

1.SVF设计

介绍:SVF框架是基于LLVM所写的,首先用clang将程序源码编译成bit code文件,然后使用LLVM Gold插件把bitcode文件整合到一个bc文件指针分析——接着进行过程间的指针分析来生成指向信息;数值流构建——基于指向信息,构建内存SSA形式,这样就能识别顶层和地址变量的def-use关系链;应用——生成的数值流信息可用于检测数据泄露和空指针引用,也可以提高数值流分析和指针分析的精确度。

1-1-SVF_framework.png

指针分析:指针分析的实现分为三个组件,分别是Graph,Rules和Solver。Graph从LLVM IR搜集抽象信息,确定对哪一部分进行指针分析;Rules定义了如何从语句中获得指向信息,例如graph中的约束;Solver确定约束的处理顺序。SVF提供了一个简洁可复用的接口,用户可以随意组合这三个组件来实现自己的指针分析。

数值流构建:基于获得的指向信息,我们实现了一个轻型的Mod-Ref Analysis,以寻找每个变量的过程间引用和被修改的副作用。给定Mod-Ref结果和指向信息,每个store/load/callsite的间接使用和定义都使用别名进行注释,每个别名都表示一组可以间接访问的抽象内存对象。注意,Open64和GCC都采用过程内的内存SSA,它是在过程内计算的,所有非局部变量都在一个单独的别名集中;相反,SVF提供了Mem Region Partitioning内存区域划分模块,允许用户定义自己的内存区域划分策略,以影响别名集的形成方式,这样在分析大型程序时可灵活地权衡可扩展性和精度。(总之,优点是能够划分模块进行分析)。

2.应用场景

(1)Source-Sink Analysis: 如检测内存泄露漏洞,检查内存分配是否最终走到释放点,给定顶层和地址变量的数值流,SABER[36,37]/SPARROW[24]能检测到泄露漏洞,通过SVF框架还能检测double-free、文件open-close错误、污点数据使用错误。

(2)指针分析: 能提高指针分析的可扩展性和准确性,例如SELFS[44],能基于数值流信息对部分程序区域实施选择流敏感指针分析;FSAM[34]是基于SVF的,能对多线程c程序进行线程交错分析,以进行稀疏流敏感指针分析。

(3)加速动态分析:动态分析,通过插桩监视程序执行行为,带来了运行时的开销。可以采用静态数值流信息来引导实施选择性插桩,这样就能消除一些不必要的插桩,降低运行开销。例如USHER[43]使用过程间数值流分析来识别多余的操作,移除该处的插桩,还能用于检测其他漏洞如空指针引用和缓冲区溢出[42];还可以与符号执行[10]和动态数据流测试[16]相结合,以更快生成更有意义的测试用例。

(4)程序调试与理解:SVF还能用于软件调试和程序理解[18,21,40],可以通过只追踪相关的数值流来寻找引发错误行为的语句,不必分析不相关的语句;可扩展和精确的过程间数值流分析也对软件可视化有帮助(code map[20])。


二、技术细节

参考,包含SVF的内存模型、指针分析、值流构建的工作原理,可以参见this doxygen manual获取更多技术细节。

1.内存模型

SVF分析LLVM bitcode并为指针分析创建内部符号表信息,符号分为两类,ValSym表示寄存器LLVM Value,高级指针;ObjSym表示抽象内存对象,地址变量指针。有些对象需特殊标记,BlackHole表示静态无法确定的符号,例如llvm中的UndefValue;ConstantObj表示常量对象。

(1)抽象内存对象

抽象内存对象(MemObj,属于全局、栈、堆对象)在内存分配点产生,用ObjTypeInfo记录该对象的类型信息。如果对象中含结构类型(StInfo),则用域信息来表示(FieldInfo,包含偏移和类型),这样就能将嵌套的结构平坦化。

(2) 程序赋值图Program Assignment Graph (PAG)

注意:结构中的字段,称为域。

SVF将LLVM指令转化为图表示——PAG,每个节点(PAGNode)表示一个指针,每条边(PAGEdge)表示两个指针间的约束。

  • PAGNode:主要分为两种,ValPN表示指针,ObjPN表示抽象对象。ObjPN的子类GepObjPN,表示一个聚合对象中的域;ValPN的子类GepValPN,表示一个引入的虚拟节点,用于在处理外部库调用时实现域敏感(如memcpy,指向结构中某个字段的指针不会显式出现在指令中)。RetNode表示过程返回,VarargNode表示过程的可变参数。
1-2-PAGnode.png
  • PAGEdge:分为以下类别。
1-3-PAGedge.png
  • AddrPE:在内存分配点(ValPN <-- ObjPN

  • CopyPE:在PHINodeCastInstSelectInstValPN <-- ValPN

  • StorePE:在StoreInstValPN <-- ValPN

  • LoadPE:在LoadInstValPN <-- ValPN

  • GepPE:在GetElementPtrInstValPN <-- ValPN

  • CallPE:从实参到形参(ValPN <-- ValPN

  • RetPE:从RetNode返回到调用点(ValPN <-- ValPN

  • PAGBuilder:PAGBuilder通过处理以下LLVM指令和表达式来生成PAG。LLVM中其他指令未做处理,但其他指令都能转换为基本的edge类型。

    • AllocaInst
    • PHINode
    • StoreInst
    • LoadInst
    • GetElementPtrInst
    • CallInst
    • InvokeInst
    • ReturnInst
    • CastInst
    • SelectInst
    • IntToPtrInst
    • ExtractValueInst
    • ConstantExpr
    • GlobalValue
(3)约束图 Constraint Graph

约束图(ConstraintGraph)是PAG的副本,用于基于包含的指针分析。PAG是SVF所有分析的基础图,PAG的边是固定的,但ConstraintGraph的边可以在求解约束时改变。

2. 指针分析

1-4-pt.png

PointerAnalysis是所有实现的基类。为了实现不同的指针分析算法,用户可以根据不同的指向数据结构(PTData)选择不同的实现。例如,流敏感和流不敏感的指针分析可以选择BVDataPTAImpl作为基类,它使用位向量作为其核心数据结构,根据指向集将指针映射到其位向量。上下文和路径敏感分析,需要对每个指针添加上下文或路径条件作为限制。实现时,可以选用CondPTAImplCondPTAImpl将条件变量映射到其指向集。

每个指针分析实现都是对图进行操作,用求解器来求解约束。例如,Andersen分析,是利用solver从基于包含关系的约束图(ConsG)上,推导出传递闭合规则。类似地,流敏感分析,是利用points-to propagation solver从稀疏值流图(VFG)推导,其中要遵循流敏感的强/弱更新规则。

3. 值流构建

1-5-svfg-framework.png
(1)内存SSA

顶层变量(寄存器)的def-use链在LLVM IR中很容易获取。load和store可以间接访问地址变量。def-use链的构建步骤:

    1. 执行指针分析以生成顶层指针(寄存器)的指向信息;
    1. load指令 p = *q 标注为函数μ(o)LoadMU),o表示q所有可能指向的变量,μ(o)表示load处对o的使用store指令*p = q 标注为函数o =χ(o)StoreCHI),o表示p所有可能指向的变量,o =χ(o)表示store处对o的定义和使用def-use;call指令标注为μ(o)CallMU)和o =χ(o)CallCHI),以记录变量o的过程间使用和定义def-use;函数entry函数exit标注为o =χ(o)EntryCHI)和μ(o)RetMU),来表示非局部变量o也即参数的传递和返回;PHI指令标注为o = mssaPhi(o)MSSAPHI),在控制流汇合点整合多个对象o的定义。
    1. 所有地址加载变量(load和store指令)转换为SSA形式,μ(o)看作是对对象o的use,o = χ(o)看作是对象o的use和def。
1-6-mssa-cha.png
(2)稀疏值流图 Sparse Value-Flow Graph(SVFG)

给定一个程序(已经过SSA转换且带有μ和χ函数标注),通过连接每个SSA变量的def-use来构造SVFG。过程间稀疏值流图(SVFG)是有向图,它捕获顶层指针和地址获取(address-taken)对象的 def-use 链。

  • SVFGNode

    • A statement (A PAGEdge)
      • AddrPE
      • CopyPE
      • GepPE
      • LoadPE
      • StorePE
    • A memory region definition
      • FormalIN // EntryCHI
      • FormalOut // RetMU
      • ActualIn // CallMU
      • ActualOut // CallCHI
      • MSSAPHI // MSSAPHI
    • A parameter
      • FormalParm // 形参 Formal Parameter
      • ActualParm // 实参 Actual Parameter
      • FormalRet // 函数返回变量 Procedure Return Variable
      • ActualRet // 调用点返回变量 CallSite Return Variable
    1-7-svfgnode-cha.png
  • SVFGEdge:表示变量从def到use的值流依赖。

    1-8svfgedge-cha.png
  • 顶层指针的直接值流

  • 地址取address-taken对象的间接值流

  • SVFG 优化:使SVFG变得更紧凑,可以移除以下node——ActualParmActualInFormalRetFormalOut


三、环境搭建

前4步,安装llvm和clang。

1.下载 LLVM-7.0.0, clang-7.0.0

2.解压

tar xf llvm-7.0.0.src.tar.xz
tar xf cfe-7.0.0.src.tar.xz
mv cfe-7.0.0.src llvm-7.0.0.src/tools/clang

3.创建目标build目录并make

mkdir llvm-7.0.0.obj
cd llvm-7.0.0.obj
cmake -DCMAKE_BUILD_TYPE=MinSizeRel ../llvm-7.0.0.src (or add "-DCMAKE_BUILD_TYPE:STRING=Release" for release version)
make -j8 #换 make V=1

4.添加LLVM和Clang的路径

export LLVM_SRC=/home/john/Desktop/kernel/llvm-7.0.0.src #路径要修改
export LLVM_OBJ=/home/john/Desktop/kernel/llvm-7.0.0.obj #路径要修改
export LLVM_DIR=/home/john/Desktop/kernel/llvm-7.0.0.obj #路径要修改

export PATH=$LLVM_DIR/bin:$PATH

#建议添加永久环境变量
$ sudo nano /etc/profile 
#NODEPATH
export CLANG_PATH=/home/john/Desktop/kernel/llvm-7.0.0.obj/bin
export PATH=$PATH:$CLANG_PATH  

安装SVF。

5.下载SVF源码

git clone https://github.com/SVF-tools/SVF.git SVF

6.用cmake构建SVF (也可以使用 build.sh 去构建release或debug版本的SVF)

cd SVF
mkdir Release-build
cd Release-build
cmake ../
make -j4

构建Debug版本:

cd SVF
mkdir Debug-build
cd Debug-build
cmake -D CMAKE_BUILD_TYPE:STRING=Debug ../
make -j4

7.添加SVF路径 ($SVF_HOME是本机的源码目录)

export PATH=$SVF_HOME/Release-build/bin:$PATH   #路径要修改
#建议添加永久环境变量
$ sudo nano /etc/profile
export SVF_HOME=/home/john/Desktop/kernel/SVF  #路径要修改
export PATH=$PATH:$SVF_HOME/Release-build/bin
source   /etc/profile

8.编译单个c程序为LLVM bitcode文件,或者用llvm-link编译和链接多个文件。若要编译复杂的真实工程则需要使用 LLVM gold plugin。 安装步骤见 this 。

clang -c -emit-llvm -g example.c -o example.bc
clang -c -emit-llvm -g example1.c -o example1.bc
clang -c -emit-llvm -g example2.c -o example2.bc
llvm-link example1.bc example2.bc

9.设置环境变量

cd $SVF_HOME
. ./setup.sh

#若安装debug版本,则以下命令
cd $SVF_HOME
. ./setup.sh debug

四、使用方法

参考

1.全程序分析

运行Andersen指针分析

wpa -ander example.bc

运行Andersen指针分析并生成全程序数值流图

wpa -ander -svfg example.bc

运行流敏感指针分析

wpa -fspta example.bc

输出格式是 dot format,可以用 zgrviewer等工具展示。

#zgrviewer安装步骤
#1.下载 http://sourceforge.net/projects/zvtm/ 
$ svn co https://svn.code.sf.net/p/zvtm/code zvtm  
#2.安装java虚拟机http://java.sun.com/
#3.安装GraphViz——http://www.graphviz.org/download/
#4.用run.sh即可运行ZGRViewer
$ java -jar target/zgrviewer-0.9.0.jar

2. Dump Graph

dump CallGraph

wpa -ander -dump-callgraph example.bc 
wpa -fspta -dump-callgraph example.bc 

dump PAG (program assignment graph)程序赋值图

wpa -ander -dump-pag example.bc 

Dump Constraint Graph

wpa -ander -dump-consG example.bc 

Dump Value-Flow Graph

wpa -ander -svfg -dump-svfg example.bc 

Dump Memory SSA

wpa -ander -svfg -dump-mssa example.bc

3.打印数据

wp a -ander -stat example.bc
wpa -fspta -stat example.bc

打印指向结果

wpa -ander -print-pts example.bc
wpa -fspta -print-pts example.bc

参考资料

主页:http://svf-tools.github.io/SVF/

github主页:https://github.com/SVF-tools/SVF

环境搭建:https://github.com/SVF-tools/SVF/wiki/Setup-Guide-(CMake)

论文链接:https://yuleisui.github.io/publications/cc16.pdf

你可能感兴趣的:(【SVF-1】SVF过程间静态数值流分析简介)