书接上文,本节内容继续讲解CPG中其余节点的DFG构建过程~
这么分节的原因主要是考虑到了大家可能会产生阅读疲劳,俗话说:时间要花在刀刃上,所以把重头戏要放在每个小节的开头,这样更有利于大家的学习~(读者内心OS:我哭死)
读后有收获的同学可以点个赞哦,要是能关注一下公众号就更好啦
大家的鼓励是笔者硬肝技术的动力!非常感谢大家文中有错误的地方欢迎大家私信/评论区指正。同时也欢迎大家将自己的想法发布在评论区,希望大家能够畅所欲言,共同进步~
欢迎点击公众号菜单栏"点击进群",与大佬一同交流技术。
不同的分支语句会产生不同的控制流,影响分支执行的条件值会通过分支产生隐式数据流,因此,CPG添加了从分支条件到分支节点的DFG边。
forEach语句(ForEachStatement
)关注以下字段:
variable: Statement
: 循环的迭代变量,可以是new变量声明,也可以是已有的变量引用
iterable: Statement
: 被循环访问的引用(如:容器对象)
可迭代值流向变量中的 VariableDeclaration
(变量声明)。由于一些语言允许任意逻辑,CPG区分了两种情况:
The
variable
is a
DeclarationStatement
对于大多数语言来说,循环语句中只允许声明一个变量(如:for(e in list)
)。DFG添加一条边:
The
variable
is another type of
Statement
这种情况,CPG会假设最后一个变量声明是用于循环的那个,所以DFG只添加一条边:
测试代码:
`package com.cpg.dfg; import java.util.Arrays; import java.util.List; public class TestForEachStatement { public static void main(String[] args) { List<String> list = Arrays.asList("hello", "SASTing"); for (String s : list) { // ForEachStatement,属性 variable表示String s,属性iterable表示list System.out.println(s); } } } `
其实就是 list 流向了 s
17.1.2 中提到的variable
(声明语句)中可以声明多个变量的情况,Java中不支持,此处不再验证
do循环语句(DoStatement)关注以下字段:
condition: Statement
: do循环语句的条件DFG边:condition流向DoStatement
测试代码:
`package com.cpg.dfg; public class TestDoStatement { public static void main(String[] args) { int i=3; do { // DoStatement,condition就是 i<10 System.out.println("Hello SASTing!"); i++; } while (i<10); } } `
while循环语句关注以下字段:
condition: Statement
: 条件
conditionDeclaration: Statement
: 条件声明语句(A declaration containing the condition in the initializer, used instead of the condition)
DFG边构建如下:
测试代码:
`package com.cpg.dfg; public class TestWhileStatement { public static void main(String[] args) { int i=10; while (i>0) { // WhileStatement,condition就是 i>0 System.out.println(i); i--; } } } `
condition --> WhileStatement
conditionDeclaration --> WhileStatement
C++ allows defining a declaration instead of a pure logical expression as condition
C++ 允许使用声明代替纯逻辑表达式作为条件定义
OK,是针对C++做的适配,与Java无关 == 与我无瓜
,skip~
for循环语句(ForStatement
)关注以下字段:
condition: Statement
: for循环的条件
conditionDeclaration: Statement
: 条件声明语句(A declaration containing the condition in the initializer, used instead of the condition)
DFG边构建如下:
condition --> ForStatement
conditionDeclaration --> ForStatement
conditionDeclaration
字段的解释与WhileStatement.conditionDeclaration
都一样,也是针对C++做的适配,skip~
if语句(IfStatement
)关注以下字段:
condition: Statement
: if语句的条件
conditionDeclaration: Statement
: 条件声明语句(A declaration containing the condition in the initialize, used instead of the condition)
DFG边构建如下:
测试代码:
`package com.cpg.dfg; public class TestIfStatement { public static void main(String[] args) { int i = 1; if (i > 0) { // IfStatement,condition 就是 i>0 System.out.println("SASTing is cool!"); } else { System.out.println("SASTing is handsome!"); } } } `
condition --> IfStatement
conditionDeclaration --> ForStatement
conditionDeclaration
字段源码注释:
C++ alternative to the condition.
所以,skip~
switch语句(SwitchStatement
)关注以下字段:
selector: Statement
: 选择器(也就是switch括号内传进去的对象)
selectorDeclaration: Statement
: 选择器的声明语句(A declaration containing the selector in the initializer, used instead of the selector)
DFG边构建如下:
测试代码:
`package com.cpg.dfg; public class TestSwitchStatement { public static void main(String[] args) { Language language = Language.JAVA; switch (language) { // SwitchStatement,selector就是 language引用 case JAVA: System.out.println("Java"); break; case CPP: System.out.println("Cpp, skip~"); default: System.out.println("not support language"); } } enum Language { JAVA, CPP } } `
selector --> SwitchStatement
selectorDeclaration --> SwitchStatement
C++ allows to use a declaration instead of a expression as selector
OK,是为了适配C++的,skip~
方法声明(FunctionDeclaration
)关注以下字段:
body: Expression
: 方法体(包括方法体内的所有语句)所有的return
表达式的流向方法声明
测试代码:
`package com.cpg.dfg; public class TestFunctionDeclaration { public String test(int a) { // FunctionDeclaration if (a > 0) { return "SASTing is handsome!"; } else { return "SASTing is cool!"; } } } `
可以看到MethodDeclaration
是FunctionDeclaration
的一个子类,表示方法声明
ReturnStatement
--> FunctionDeclaration字段声明(FieldDeclaration
)关注以下字段:
initializer: Expression?
: 字段初始化值(若没有初始化则该字段为null
)数据流从字段初始值流向字段声明,字段声明流向下一次字段读取
测试代码:
`package com.cpg.dfg; public class TestFieldDeclaration { public String name = "SASTing"; public void test() { System.out.println(name); // read access of the field name } } `
initializer --> FieldDeclaration
FieldDeclaration --> next read access of the field
tips: 实际上,在CPG中的DFG-2(加链接,待修改)中讲到
Reference
的DFG构建时,若是一个字段引用,那么该引用的refersTo
属性就是此处的字段声明。
变量声明(VariableDeclaration
)关注以下字段:
initializer: Expression?
: 变量的初始化值数据流从initializer
流向变量声明,在对变量写入值之前,变量声明的值会流向所有变量读取处
测试代码:
`package com.cpg.dfg; public class TestVariableDeclaration { public static void main(String[] args) { String str = "init str"; // initializer of str System.out.println(str); // read access of str readStr(str); // read access of str str = "SASTing"; // write access of str System.out.println(str); // read access of str, str写入值后的读取,故VariableDeclaration不会流向此处 } private static void readStr(String str) { System.out.println(str); } } `
initializer --> VariableDeclaration
VariableDeclaration --> all read access of str before write access of str
可以看到 VariableDeclaration 没有流向第9行,因为第8行有str
写入操作
元组声明(TupleDeclaration
)关注以下字段:
initializer: Expression?
: 初始化值
element: List<VariableDeclaration>
: 元组中的元素列表
initializer
流向element
,在对变量写入值之前,变量声明的值会流向所有变量读取处
Java并不支持直接声明元组类型,需要借助Google Guava库中的Pair
和Triple
或者手动包装一个类似元组的类。故TupleDeclaration
的边验证也skip~
赋值语句(Assignment
)关注以下字段:
value: Expression
: 赋值语句的 rhs (右操作数)
target: AssignmentTarget
: 赋值语句的 lhs (左操作数)
DFG边的构建在 CPG中的DFG-1 中提到的BinaryOperator
operator
为=
的构建方式一致
测试代码:
`package com.cpg.dfg; public class TestAssignment { public static void main(String[] args) { int a = 1; a = 2; // Assignment } } `
先看其target
是一个引用,其内存地址是7469
value的nextDFG
指向了reference,地址也是7469
,所以存在value --> target 的边
CPG中DFG的构建模块到这里就结束啦,东西不难,就是细节比较多,建议大家对这块内容不要死记硬背,在使用过程中若遇到了DFG,再回过头来看看其DFG的构建过程即可,慢慢就熟悉了~
VLC视频播放器(https://www.videolan.org/vlc/index.an.html)
支持的操作系统:Mac,Linux,Windows,Android,IOS
支持市面上几乎所有的视频格式
免费,无广告,无捆绑
CPG中的函数摘要
当遇到CPG无法分析的方法时,数据流会中断,会影响分析精度。因此CPG支持自定义配置函数摘要,用户可以通过配置文件添加函数摘要,使数据流完整。
文中的测试样例已同步****github仓库,欢迎大家多多star,仓库地址如下:
https://github.com/HaHarden/CPGPractise
https://github.com/HaHarden/BenchmarkJava-Pro
进群方式:
1. 扫码进群
2. 点击公众号底部菜单栏"点击进群",加笔者好友(备注****进群)