代码解析工具cpg
cpg 是一个跨语言代码属性图解析工具,它目前支持C/C++ (C17), Java (Java 13)并且对Go, LLVM, python, TypeScript也有支持,在这个项目的根目录下:
-
cpg-core为cpg解析模块的核心功能,主要包括将代码解析为图,core模块只包括对C/C++/Java的支持。
-
cpg-analysis为基于代码属性图的代码分析规则,比如ValueEvaluator主要基于DFG的回溯遍历计算某一表达式的值。
-
cpg-neo4j将解析结果导入到neo4j数据库。
-
cpg-language-xxx为对xxx语言的支持。
-
cpg-console包含针对用户端的功能,比如bug检测:OutOfBoundsCheck,NullPointerCheck。
cpg将编程语言抽象为1个特性(Trait)集合,语言特性是一组编程语言所共有的特性或特性。任何支持它们的语言都应该实现所需的trait接口。示例可以是对指针的支持、对模板或泛型的支持。
1.Graph Structure
这一部分主要介绍cpg提供的图结构,这些结构旨在提供大多数面向对象语言(如C++、Java或Go)中的语言特性的超集。这包括结构元素,例如函数、方法、类以及表达式/语句,例如调用、运算符、文本或条件。
1.1.Abstract Syntax Tree
树的根是TranslationUnitDeclaration节点,它表示一个文件中包含的代码。树中存在不同类型的AST子节点,每个子节点表示程序中的不同语义:
-
Structural Entities
表示为代码提供其结构的实体。比如代表namespaces
,classes
,structs
的结点。 -
Value Declarations
用于建模局部变量、参数、函数、方法和构造函数。 -
Nodes of Program Execution
它为语句和表达式建模。-
语句是按顺序执行的语法单元,因此构成命令式程序的程序逻辑。
-
相比之下,表达式是计算值的语法单位,属于特定的值类型。表达式可以是嵌套的,也可以是语句的子级。
-
1.2.Control Flow and Evaluation Order
cpg将控制流图(CFG)的概念改编为评估顺序图(evaluation order graph, EOG),它按照语句和表达式被求值的顺序相互连接,以在更细粒度级别上表示控制流。这对于正确捕获来自表达式内部执行顺序的副作用是必要的。可以说EOG是expression-level CFG。
比如对下面代码:
int a = 2 + 3;
a = 0;
a += 1;
EOG为:
-
绿框为3个语句对应的AST根节点,蓝框为其下AST子结点。
-
蓝线连接同一语句下的AST结点,绿线连接不同语句的AST结点。
可以看出:
-
EOG的连接顺序为AST结点后序遍历顺序
-
连接的结点都是对应的表达式或者语句访问顺序。
通常,EOG的构造对应后序遍历,当然也存在一些例外情况:
-
显式更改程序控制流的构造函数,如
if
或其他条件表达式。 -
表示不按代码出现的顺序执行的代码的节点,例如,在
for (initializer; condition; iteration) body();
中body();
在initializer
之后执行,在iteration
之前执行。
分支语句的根节点在分支表达式(例如条件或选择器)之后和分支目标之前连接,以允许已经遍历分支表达式的算法在必须处理分支之前获得关于语义根的信息。
对于下面分支代码:
if (a == 4)a += 2;
elsea += 3;
return 0;
其EOG如下:
1.3.Data Flow
处理数据的操作或实体在CPG内的数据流图(DFG)中表示。要对程序中的数据流进行建模,以下DFG边连接节点:
-
(1).如果父结点的值取决于子结点的值,则子对象包含其父结点的边。比如
BinaryOperator:2 + 3
指向其父结点VariableDeclaration:a = 2 + 3
。 -
(2).如果语句对被引用变量进行写入,那么被引用变量是DF edge的起始结点;如果语句读取被引用变量,那么被引用变量是DF edge的开始。比如
a = 1; if (a == 4) xxx
中存在从DeclaredReferenceExpression: a
(a = 1;
中的a
) 指向BinaryOperator: a== 4
的DF edge。 -
(3).函数调用时会建立从
CallExpression
对应的被引用变量到被调用参数之间的DF edge,以及被调用者定义和该CallExpression
之间也会建立DF edge,如下面示例所示:DeclaredReferenceExpression
和ParamVariableDeclaration
之间有数据流,FunctionDeclaration
和CallExpression
之间也有数据流。-
绿边为data flow edge
-
蓝边为普通AST edge(这里只包含了部分AST信息)
-
-
(4).如果控制流敏感(control-flow-sensitive)的数据流分析被执行,那么对于一个变量 (
DeclaredReferenceExpression
),在它最后一次被写入的地方和对应引用的位置会有1条数据流 -
(5).对于控制流不敏感的数据流分析,变量引用 (
DeclaredReferenceExpression
)和变量定义(VariableDeclaration
)之间存在数据流。
对于具体DFG建立规则,可以参考cpg DFG
1.4.Additional program semantics
-
程序和所用语言的类型系统是通过添加其复杂的层次结构作为节点和边来建模的,形成一个类型子图,这有助于通过允许根据细微差别的语言语义进行区分,对语言级语义进行建模,以提高精度。
-
REFERS_TO
连接引用和定义。(每个DeclaredReferenceExpression
都有1个成员变量refersTo指向对应变量定义的位置) -
INVOKES
连接调用语句和调用对象。(每个CallExpression
都有成员变量invokeEdges)指向其可能的调用对象。
2.核心架构和实现
CPG项目由围绕一个库构建的多个工具组成,用于以基于Java/Kotlin的开源实现3的形式对源代码进行迭代图形构建。图1描述了将源代码转换为图形表示的工作流,以及用户对其进行的可视化和分析。
代码解析的核心部分由 cpg-core
完成,用户输入由 cpg-console
完成。
2.1.语言前端(Language Frontends)
源代码文件通过其文件扩展名发送到特定语言的前端。然后,前端可以使用针对特定语言的AST解析库解析源代码(针对C/C++使用eclipse CDT,针对Java采用Java Parser)。语言前端应执行以下任务:
-
特定语言的AST被转换为独立于语言的cpg AST结构,以方便建立独立于特定语言的图结构
-
隐式存在但未位于源代码中的实体将显式添加到图中,例如隐式
this
字段和缺少的返回语句(void
函数,比如下面代码片段中swap
函数在解析后补充了ReturnStatement:return;
)
void swap(int *p, int *q) {int t = *p;*p = *q;*q = t;
}int main() {int a = 2, b = 1;swap(&a, &b);int c = a + 1;return 0;
}
-
标识符被收集在scope tree中,以便以后以模糊的方式解析对名称的访问。在cpg AST的每个结点中,都有一个局部变量scope指向一个Scope对象,每个
scope
对象会用1个ASTNode来标识。以上面代码为例:-
整个代码对应的
TranslationUnitDeclaration
会对应到一个GlobalScope(记作globalScope
)对象中,该GlobalScope
对象对应的ASTNode就是该TranslationUnitDeclaration
-
swap
和main
函数对应的FunctionDeclaration
的scope
域指向globalScope
-
swap
和main
函数定义下的CompoundStatement
(函数体) 的scope
域分别指向2个FunctionScope(分别记作functionScope1, functionScope2
),functionScope1, functionScope2
对应的ASTNode分别为2个FunctionDeclaration
结点 -
int t = *p;
,int c = a + 1;
的scope
域分别指向2个BlockScope,这2个BlockScope
对应的ASTNode分别为2个函数定义下的CompoundStatement
-
在 cpg-core
中,已经实现了针对C/C++/Java的前端,而 cpg-language-xxx
实现了针对xxx语言的前端。
2.2.ScopeManager
在大多数编程语言中,名称的声明不是全局有效的,但其有效性仅限于与语言构造相关的区域,例如类或函数。这个有效区域被称为作用域(scope)。当前端遍历AST节点时,作用域管理器跟踪当前活动的作用域堆栈。这允许按绝对或相对名称跟踪和解析声明,并管理绑定到封闭语言功能(例如循环、try
语句)范围的控制流跳转。在语言前端构建作用域之后,作用域管理器持有多个作用域树。
在全局存在一个ScopeManager对象,通过 HashMap
的方式将ASTNode映射为 Scope
2.3.Passes
cpg前端产生部分连接的AST树。然后,通过隐式执行信息和程序语义(如使用引用、数据流和求值顺序),使用Passes来丰富cpg。
比如:
-
DFGPass负责生成DFG。
-
ControlFlowSensitiveDFGPass负责生成control-flow sensitive DFG。
-
EvaluationOrderGraphPass负责生成EOG。
这些语义是在独立于语言的AST节点之间构建的,并且它们本身独立于特定编程语言。然而,它们仍然允许特定语言的定制。
当需要graph中的语义时,某些 Pass
取决于其他 Pass
的先前执行。比如 ControlFlowSensitiveDFGPass
依赖于 DFGPass, EvaluationOrderGraphPass
。后2者只遍历了AST,而 ControlFlowSensitiveDFGPass
还需遍历EOG。
最后,Pass
也支持推理。对于在源代码中不直接可见的实体,节点将被添加并标记为 IMPLICIT
,如果缺少源代码,则为 INFERRED
3.程序分析
cpg在 cpg-analysis
下定义了一些程序分析规则,包括计算表达式的值、不可达代码分析等等。基本是通过图的遍历实现的,这里就介绍以下表达式求值。
表达式求值有点类似于常量传播,不过区别在于这里是通过Data Flow Graph的遍历实现,对应的代码为 ValueEvaluator.evaluate,具体规则参考evaluateInternal函数(还有1个更高级的版本MultiValueEvaluator):
-
返回值主要为常量值或者
canNotResolve
,如果不能确定值的变量,那么返回的可能就是canNotResolve
。 -
如果访问的AST结点为 Literal类型,那么直接返回其值。
-
如果是
DeclaredReferenceExpression
,那么沿着DFG回溯,这里需要注意的是,data flow必须唯一,如果有多个DFG边指向该变量引用,也就是该变量可能值有多个,那么返回canNotResolve
。 -
对于算术运算表达式,则先分别计算其左右值,如果不是
canNotResolve
,那么直接计算其值。
4.Bug检测
这一部分为cpg的部分用户功能,放在 cpg-console
模块中。
cpg目前支持2种类型的bug检测,out of bound和空指针访问,这2种bug clang static analyzer(CSA)已有实现,不过CSA基于符号执行,cpg基于data flow graph的反向遍历。
4.1.Out of Bound Check
CSA中的实现可参考知乎介绍,clang对应的代码为ArrayBoundChecker,ArrayBoundCheckerV2。
可以检查出由数组访问造成的溢出读写,比如 array[idx] = 1;
,但是数组访问必须满足以下条件:
-
idx
必须是常量表达式(索引可以用变量,但是其值必须是常量) -
数组长度必须是常量,且长度通过
new Type[n];
定义,n
为常量表达式
首先给出以下示例:
#include <iostream>
using namespace std;int main() {int *a = new int[9 + 1];int i = 10;int idx = i + 1;a[idx] = 13;return 0;
}
其中有几个比较重要的AST结点类型:
- ArrayCreationExpression: 表示由
new Type[n]
组成的AST结点,比如:
- ArraySubscriptionExpression:表示数组访问语句
array[index]
,array
和index
都属于Expression
类(通常为DeclaredReferenceExpression
)
-
VariableDeclaration: 变量定义结点,在图1中已有示例
-
DeclaredReferenceExpression: 变量引用结点,表示对变量的使用,每个
DeclaredReferenceExpression
都会有一个成员变量refersTo
指向定义该变量的VariableDeclaration
结点。比如图2中的DeclaredReferenceStatement: a
指向图1中的VariableDeclaration: *a = new int[9 + 1];
检查数组越界访问按以下方式进行,检查对象为 ArraySubscriptionExpression
结点,对于某一个 ArraySubscriptionExpression
:
-
取出改数组表达式的索引(示例中为图2
DeclaredReferenceStatement:idx
),计算其值(示例中值为11
),如果不是常量则退出。 -
找到表达式数组对应的
DeclaredStatementExpression
(示例中为图2DeclaredStatementExpression:a
),找到其对应的VariableDeclaration
(示例中为图1VariableDeclaration: *a = new int[9 + 1];
),查找有没有ArrayCreationExpression
,没有则退出(int a[100];
这种语句都不行)。 -
Type[n]
中的n
对应着数组长度表达式,计算其值(示例中为10
),不是常量则退出。 -
比较索引和长度的值,如果索引 >= 长度则报出越界访问。
参考第3部分,表达式的计算核心是回溯DFG,对于计算索引 idx
的值,上述示例比较重要的DFG包括下面4个,计算的过程是从最后1个data flow往前回溯
-
VariableDeclaration:i = 10 --------> DeclaredReferenceExpression:i
,其中VariableDeclaration:i = 10
对应的initializer
为Literal: 10
,因此DeclaredReferenceExpression:i
的结果为10
-
VariableDeclaration:idx = i + 1 --------> DeclaredReferenceExpression:idx
(从语句int idx = i + 1;
指向a[idx] = 13;
),计算idx时,追溯到VariableDeclaration:int idx = i + 1
,对应的initializer
为BinaryOperator: i + 1
,左值为DeclaredReferenceExpression:i
,右值为Literal:1
,递归回溯后,左值结果为10
,因此DeclaredReferenceExpression:idx
的值为11
没有用到的data flow包括:
-
Literal:10 --------> VariableDeclaration:i = 10
-
BinaryOperator:i + 1 --------> VariableDeclaration:idx = i + 1
同理,可以计算出 a
的长度为 10
,因此存在越界访问。
4.2.Null Pointer Check
CSA中的实现可参考NullabilityChecker, GoSSIP团队也在CSA的基础上实现了新的空指针解引用检测算法NewDereferenceChecker。
在cpg中空指针的检测规则比较简单,如果出现指针访问(数组,成员变量,成员方法),对其对应指针变量 base
进行求解,如果可能为 nullptr
(必须由 nullptr
初始化,NULL
和默认初始化都不行)。
求解是对 base
变量所有的DFG前驱进行求解,下面示例也会检测出 s->i = 1;
存在空指针访问,这是由于存在错误数据流 VariableDeclaration:*s = nullptr --------> DeclaredReferenceExpression:s
(line 11)
struct S1 {char c1; //1个字节int i; //4个字节char c2; // 1个字节
};int main() {struct S1 *s = nullptr; // 可以识别nullptr关键字,NULL不行s = new S1();s->i = 1;return 0;
}
5.参考文献
paper地址
@article{Weiss_A_Language-Independent_Analysis_2022,author = {Weiss, Konrad and Banse, Christian},doi = {10.48550/arXiv.2203.08424},title = {{A Language-Independent Analysis Platform for Source Code}},year = {2022}
}
相关文章:

代码解析工具cpg
cpg 是一个跨语言代码属性图解析工具,它目前支持C/C (C17), Java (Java 13)并且对Go, LLVM, python, TypeScript也有支持,在这个项目的根目录下: cpg-core为cpg解析模块的核心功能,主要包括将代码解析为图,core模块只包括对C/C/Ja…...
Linux虚拟机部署Java环境-Jdk-Mysql
Linux虚拟机部署 author hf 1.安装 电脑安装x-shell工具,然后使用堡垒机基础控件windows版进行安装扫描,最后点击自动检测,保证能扫描到X-shell工具的安装路径 使用堡垒机登录快照夏选择工具点击Xshell进行连接 查看linux版本 root:~# ca…...

每日学术速递2.9
CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV、cs.AI、cs.LG、cs.IR 1.Graph Signal Sampling for Inductive One-Bit Matrix Completion: a Closed-form Solution(ICLR 2023) 标题:归纳单比特矩阵完成的图信号采样&am…...

【Linux】进程优先级 | 进程的切换 | 环境变量详解
🤣 爆笑教程 👉 《看表情包学Linux》👈 猛戳订阅 🔥 💭 写在前面:我们先讲解进程的优先级,探讨为什么会存在优先级,以及如何查看系统进程、进程优先级的修改。然后讲解进程的切…...

leaflet 实现左卷帘效果 (代码示例045)
第045个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+leaflet中实现左卷帘效果,这里主要引用了leaflet-side-by-side这个插件,直接调用的话,CSS方面有些问题,需要自行调整一下。 直接复制下面的 vue+leaflet源代码,操作2分钟即可运行实现效果 文章目录 示例效果配…...

程序的翻译环境和执行环境
程序环境和预处理🦖程序的翻译环境和执行环境🦖详解编译链接🐳 翻译环境🐳 详解编译过程🐳 运行环境🦖预处理详解🐳 预定义符号🐳 #define🦀 #define 定义标识符…...
2023最新量化优选股票参考(2.9)
还是周一发的那些股票(可以看我周一的文章),安心持仓就好,跑赢指数是大概率的事情,也大概率获得正收益。 其实我知道大家都没法全天一直看盘操作,毕竟要工作,我也是一样,没法一直看盘…...

深眸科技以科技赋能智慧物流搭建,实现周转箱拆垛作业智能化
数字化时代下市场竞争的核心要素转化为科技的竞争,智能化技术的投入是企业占据市场竞争绝对优势的重要支撑。深眸科技凭借轻辙视觉引擎实现周转箱拆垛作业的智能化突破。人力成本增加,企业积极转变特别是在后疫情时代,人力成本迅猛增加&#…...
R数据分析:孟德尔随机化中介的原理和实操二
delta方法 上面的流程跑通之后,对于中介分析,我们需要报告间接效应的估计值和置信区间,还有中介比例的估计值和置信区间,类似下面的这样: 但是其实我们是光跑孟德尔是得不到上面的需要的值的(比如间接效应…...
【SQL开发实战技巧】系列(十二):三问(如何对字符串字母去重后按字母顺序排列字符串?如何识别哪些字符串中包含数字?如何将分隔数据转换为多值IN列表?)
系列文章目录 【SQL开发实战技巧】系列(一):关于SQL不得不说的那些事 【SQL开发实战技巧】系列(二):简单单表查询 【SQL开发实战技巧】系列(三):SQL排序的那些事 【SQL开发实战技巧…...

数据库模式(schema)是什么?
在数据库的术语中,模式(schema)是一个逻辑概念,用于组织数据库中的对象。模式中的对象通常包括表、索引、数据类型、序列、视图、存储过程、主键、外键等等。 模式可以为数据库对象提供逻辑隔离功能,不用应用程序可以…...

出现failed to load steamui.dll如何解决?好的修复方法推荐
当你电脑突然出现failed to load steamui.dll的时候,你是否一脸懵逼?根本不知道发生啥时候,突然就会这样报错,其实造成这个原因,主要是因为问题出在steam上,我们还是有很多种方法可以解决的,今天…...
js 原生事件触发
var event nullevent new Event(input);document.querySelectorAll("input[placeholder点击网址 选择远端数据字典网址]")[0].dispatchEvent(event)...

Nacos安装配置(二)
目录 一、概述 二、Nacos 安装 A)Debian11 1)软件环境 2)下载源码或者安装包 3)mysql配置 4)启动服务器 B) Debian11 1) 安装JDK 2) 安装Maven 3) 安装Nacos2 4) 修改访问参数(/conf/applicati…...

【Linux基础知识】
Linux基础知识 Linux基础知识 系统目录结构 /bin: 命令和应用程序。 /boot: 这里存放的是启动 Linux 时使用的一些核心文件,包括一些连接文件以及镜像文件。 /dev : dev 是 Device(设备) 的缩写, 该目录下存放的是 Linux 的外…...

【王道数据结构】第七章| 查找 | 树
目录 一、查找 1、查找概念 2、顺序查找 3、折半查找 4、分块查找 二、树 1、B树 2、B树的基本操作 3、B树 4、散列查找及其性能分析 5、散列查找及性能分析 一、查找 1、查找概念 查找:在数据集合中寻找满足某种条件的数据元素的过程称为查找。查找…...

VBA提高篇_19 可选参数Optional_ IsMissing _MSgbox
文章目录1. 可选参数Optional2.IsMissing判断参数是否提供,只能判断变体类型3. 使用 : 可以按参数名传递参数 a:1,c:34.Msgbox 常用参数5.VBA颜色常量表1. 可选参数Optional Optional 代表本参数是可选项 False ; 代表参数若不指定,则默认为False Function mySumProduct(r As R…...

【子网划分】求子网网络前缀、子网地址、每个子网可以分配给主机使用的最小地址和最大地址
1、某单位分配到一个地址块152.7.77.0/24,现在需要进一步划分为4个一样大的子网。(10分) 问题: (1) 每个子网的网络前缀有多长? (2) 每一个子网中有多少个地址? (3) 每一个子网的网络地址是什么?…...

网络协议安全
网络协议安全网络协议ISO/OSI七层模型OSI模型与TCP/IP模型网络接口与互联网层安全传输层与应用层安全传输层协议-TCP协议传输层协议-UDP协议网络协议 ISO/OSI七层模型 物理层 作用:定义物理链路的前期、机械、通信规程、功能要求等将比特流庄换成电压典型物理层设备…...

ImportError: /lib64/libm.so.6: version `GLIBC_2.23‘ not found问题解决方法
1.环境:Centos7,GCC version 9.1.0,python3.7,TensorFlow1.14.0.因为/usr/lib64/libstdc.so.6: version CXXABI_1.3.8 not found问题,我将GCC版本升级到了9.1.0,但是运行TensorFlow的时候出现了ImportError…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...

基于TurtleBot3在Gazebo地图实现机器人远程控制
1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...
Web中间件--tomcat学习
Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机,它可以执行Java字节码。Java虚拟机是Java平台的一部分,Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...
4. TypeScript 类型推断与类型组合
一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式,自动确定它们的类型。 这一特性减少了显式类型注解的需要,在保持类型安全的同时简化了代码。通过分析上下文和初始值,TypeSc…...

逻辑回归暴力训练预测金融欺诈
简述 「使用逻辑回归暴力预测金融欺诈,并不断增加特征维度持续测试」的做法,体现了一种逐步建模与迭代验证的实验思路,在金融欺诈检测中非常有价值,本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...

LabVIEW双光子成像系统技术
双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制,展现出显著的技术优势: 深层组织穿透能力:适用于活体组织深度成像 高分辨率观测性能:满足微观结构的精细研究需求 低光毒性特点:减少对样本的损伤…...