From b465096f810abfd90de6f2e092f5dcd62b6b9390 Mon Sep 17 00:00:00 2001 From: yxxx Date: Wed, 6 Apr 2022 17:54:53 +0800 Subject: [PATCH 01/36] add taint analysis --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index e7d74ef..510251e 100644 --- a/README.md +++ b/README.md @@ -29,11 +29,17 @@ you can use the docker we builded like docker-compose.yml - [ ] RTA - [ ] 指针分析 - [x] 上下文无关指针分析 + - [ ] 一阶上下文调用点敏感指针分析 + - [ ] 一阶上下文对象敏感指针分析 + - [ ] 一阶上下文类型敏感指针分析 - [ ] 污点分析 + - [x] 上下文无关ptaint ## Usage + 见docs文件夹 ## Acknowledgement + - 感谢南大的李樾和谭添两位老师,通过他们开设的[程序分析课程](https://pascal-group.bitbucket.io/teaching.html)入门了静态分析这一领域。 - 感谢[Doop](https://bitbucket.org/yanniss/doop) , 提供了soot-fact-generator.jar 。 From 776d25c0f70e212014bd10a116e99119fcbc4e3b Mon Sep 17 00:00:00 2001 From: yxxx Date: Sun, 24 Apr 2022 09:13:44 +0800 Subject: [PATCH 02/36] add caller blacklist --- logic/cha.dl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/logic/cha.dl b/logic/cha.dl index a0be1c0..1e592fe 100644 --- a/logic/cha.dl +++ b/logic/cha.dl @@ -4,6 +4,7 @@ .decl SinkDesc(simplename:symbol, class:Class) .decl SinkMethod(method:Method) .decl EntryMethod(method:Method) +.decl BanCaller(method:Method) .decl CallGraph(insn:Insn, caller:Method, callee:Method) @@ -22,18 +23,21 @@ Reachable(method, 0) :- Reachable(callee, n+1), CallGraph(insn, caller, callee) :- Reachable(caller, n), + !BanCaller(caller), n < MAXSTEP, SpecialMethodInvocation(insn, _, callee, _, caller). Reachable(callee, n+1), CallGraph(insn, caller, callee) :- Reachable(caller, n), + !BanCaller(caller), n < MAXSTEP, StaticMethodInvocation(insn, _, callee, caller). Reachable(callee, n+1), CallGraph(insn, caller, callee) :- Reachable(caller, n), + !BanCaller(caller), n < MAXSTEP, VirtualMethodInvocation(insn, _, method, receiver, caller), MethodInfo(method, simplename, _, _, _, descriptor, _), @@ -45,6 +49,7 @@ CallGraph(insn, caller, callee) :- Reachable(callee, n+1), CallGraph(insn, caller, callee) :- Reachable(caller, n), + !BanCaller(caller), n < MAXSTEP, StaticMethodInvocation(insn, _, method, caller), MethodInfo(method, "doPrivileged", _, "java.security.AccessController", _, _, _), From 0905027e57ae5aa34aba1fa3dc0765b62a032e2d Mon Sep 17 00:00:00 2001 From: yxxx Date: Sun, 24 Apr 2022 16:24:43 +0800 Subject: [PATCH 03/36] add rta --- .gitignore | 1 + docs/callgraph.md | 77 +++++++++++++++++++--- example/cha-example-1.dl | 22 +------ logic/rta.dl | 137 +++++++++++++++++++++++++++++++++++++++ neoImportCall.sh | 2 +- 5 files changed, 207 insertions(+), 32 deletions(-) create mode 100644 logic/rta.dl diff --git a/.gitignore b/.gitignore index 6b37dc9..0fa412e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.csv output/*.csv .DS_Store +.idea diff --git a/docs/callgraph.md b/docs/callgraph.md index eecab5f..38f7d74 100644 --- a/docs/callgraph.md +++ b/docs/callgraph.md @@ -16,7 +16,7 @@ SpeicalMethodInvocation和StaticMethodInvocation的被调用的方法在编译 为了能够分析大的应用,这里限制了调用长度,在Reachable(method, step) 中step 表示从入口函数到method的调用步数。 -``` +```dl // 先根据Etrypoint解析出具体的method,加入Reachable Reachable(method, 0) :- EntryPoint(simplename, descriptor, class), @@ -43,7 +43,7 @@ CallGraph(insn, caller, callee) :- CHA调用图算法认为,receiver在实际运行的过程中的类型可以是其声明类型的任意非abstract子类。 -``` +```dl Reachable(callee, n+1), CallGraph(insn, caller, callee) :- Reachable(caller, n), @@ -67,13 +67,67 @@ CallGraph(insn, caller, callee) :- 在benchmark使用样例见[cha-exmaple-1.dl](../example/cha-example-1.dl) 创建benchmark facts时可以加上--facts-subset 参数,只生成bechmark中的facts不再连依赖库中的一起解析。具体命令如下 -``` + +```bash java8 -jar ~/code/soot-fact-generator/build/libs/soot-fact-generator.jar -i Benchmark-1.0-SNAPSHOT.jar --full -l /Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/rt.jar -d callgraphtest --allow-phantom --generate-jimple --facts-subset APP ``` ## Rapid type analysis (RTA) -待补充 +RTA是CHA的改进版本,RTA调用图算法认为,receiver在实际运行的过程中的类型不仅要满足是声明类型的子类,而且这个子类还要已经创建过实例。 + +以 https://github.com/BytecodeDL/Benchmark/blob/main/src/main/java/com/bytecodedl/benchmark/demo/VirtualCallDemo1.java 为例介绍 CHA和RTA的区别 + +```java +public static void main(String[] args) { + VirtualCallInterface1 vcall2 = new VirtualCallDemo2(); + VirtualCallDemo1 vcall1 = new VirtualCallDemo1(vcall2); + VirtualCallInterface1 varParent = vcall1.getParent(); + String source = vcall1.source(); + varParent.foo(source); +} +``` + +在解析`varParent.foo(`时 +CHA认为 receiver `varParent`的变量为其声明类型VirtualCallInterface1的所有子类,也就是VirtualCallDemo1, VirtualCallDemo2, VirtualCallDemo3 所以`varParent.foo(`会被解析成也就是VirtualCallDemo1#foo,VirtualCallDemo2#foo,VirtualCallDemo3#foo,存在两条误报边。 +RTA认为 receiver `varParent`的变量为其声明类型VirtualCallInterface1的所有子类 和 已实例化类型的交集(两条new语句中的类型), +也就是{VirtualCallDemo1, VirtualCallDemo2, VirtualCallDemo3} 和 {VirtualCallDemo1, VirtualCallDemo2} 的交集, +即 {VirtualCallDemo1, VirtualCallDemo2}, 所以`varParent.foo(`会被解析成VirtualCallDemo1#foo,VirtualCallDemo2#foo 减少了一条误报。 + +RTA对应的规则和CHA基本一致,总共有两处改变, + +一处是增加个新的 relation `InstantiatedClass(insn:Insn, class:Class)` 表示reachable中方法已经实例化的类 + +另一处改变是在解析虚拟方法时,对subeqclass增加了限制,要求subeqclass还要在InstantiatedClass中。 + +具体的规则如下 + +```dl +// 如果method可达 +// 且method中创建heap +// heap 的类型是 class +// 那么class就是已创建实例的类型 +InstantiatedClass(insn, class) :- + Reachable(method, _), + AssignHeapAllocation(insn, _, heap, _, method, _), + NormalHeap(heap, class). + + Reachable(callee, n+1), + CallGraph(insn, caller, callee) :- + Reachable(caller, n), + !BanCaller(caller), + n < MAXSTEP, + VirtualMethodInvocation(insn, _, method, receiver, caller), + MethodInfo(method, simplename, _, _, _, descriptor, _), + VarType(receiver, class), + SubEqClass(subeqclass, class), + !ClassModifier("abstract", subeqclass), + // 限制subeqclass是已创建实例的类型 + InstantiatedClass(_, subeqclass), + Dispatch(simplename, descriptor, subeqclass, callee). +``` + +CHA只会存在误报,但是RTA既可能存在误报也可能存在漏报。 ## Visualization @@ -82,27 +136,30 @@ java8 -jar ~/code/soot-fact-generator/build/libs/soot-fact-generator.jar -i Benc 经测试,通过`neo4j-admin import` 导入的效率最高。 [参考链接](https://neo4j.com/docs/operations-manual/current/tutorial/neo4j-admin-import/) 导入neo4j docker的步骤为: + 1. 执行下面命令将结果输出到output文件夹 - ``` + + ```bash souffle -I ~/code/ByteCodeDL/logic -F factsdir -D ~/code/ByteCodeDL/output ~/code/ByteCodeDL/example/cha-example-1.dl ``` + 2. 执行bash importOutput2Neo4j.sh neoImportCall.sh dbname 3. 最后访问 http://ip:7474 登录 neo4j/bytecodedl - 注意上述方式只能脱机导入,也就是databasname不能是正在使用的,需要指定个新的,由于社区版neo4j的限制,不能进行多数据库链接,需要通过更改配置文件切换数据库。 可以通过 -``` +```cypher MATCH p=(entry:entrypoint)-[*]->() where entry.method contains "virtualcall.vc2" RETURN p ``` + 查询入口函数开始的调用图,效果如下,点击节点可以在右侧看到详情 ![cha-vc2](images/cha-vc2.png) - - ## Reference - https://pascal-group.bitbucket.io/lectures/Inter.pdf -- https://neo4j.com/docs/operations-manual/current/tutorial/neo4j-admin-import/ \ No newline at end of file +- https://neo4j.com/docs/operations-manual/current/tutorial/neo4j-admin-import/ +- http://web.cs.ucla.edu/~palsberg/paper/oopsla00.pdf +- https://people.cs.vt.edu/ryder/515/f05/lectures/OOPLs-CallGraphConstr9.pdf \ No newline at end of file diff --git a/example/cha-example-1.dl b/example/cha-example-1.dl index 11dd251..415df41 100644 --- a/example/cha-example-1.dl +++ b/example/cha-example-1.dl @@ -9,24 +9,4 @@ EntryPoint(simplename, descriptor, class) :- MethodInfo(_, simplename, _, class, _, descriptor, _), simplename = "main", - descriptor = "([Ljava/lang/String;)V". - -.decl CallNode(node:Method, label:symbol) -.output CallNode - -CallNode(node, "method") :- - Reachable(node, n), - n > 0. - -CallNode(node, "entrypoint") :- - Reachable(node, n), - n = 0. - -.decl CallEdge(caller:Method, callee:Method) -.output CallEdge - -CallEdge(caller, callee) :- - CallGraph(_, caller, callee). - -// ouput callgraph -.output CallGraph \ No newline at end of file + descriptor = "([Ljava/lang/String;)V". \ No newline at end of file diff --git a/logic/rta.dl b/logic/rta.dl new file mode 100644 index 0000000..f2720cb --- /dev/null +++ b/logic/rta.dl @@ -0,0 +1,137 @@ +.comp RTA{ + .decl EntryPoint(simplename:symbol, descriptor:symbol, class:Class) + .decl Reachable(method:Method, step:number) + .decl SinkDesc(simplename:symbol, class:Class) + .decl SinkMethod(method:Method) + .decl EntryMethod(method:Method) + .decl BanCaller(method:Method) + + .decl CallGraph(insn:Insn, caller:Method, callee:Method) + .decl InstantiatedClass(insn:Insn, class:Class) + + SinkMethod(method) :- + SinkDesc(simplename, class), + SubEqClass(subeqclass, class), + !ClassModifier("abstract", subeqclass), + MethodInfo(method, simplename, _, subeqclass, _, _, _). + + EntryMethod(method), + Reachable(method, 0) :- + EntryPoint(simplename, descriptor, class), + Dispatch(simplename, descriptor, class, method). + + Reachable(callee, n+1), + CallGraph(insn, caller, callee) :- + Reachable(caller, n), + !BanCaller(caller), + n < MAXSTEP, + SpecialMethodInvocation(insn, _, callee, _, caller). + + Reachable(callee, n+1), + CallGraph(insn, caller, callee) :- + Reachable(caller, n), + !BanCaller(caller), + n < MAXSTEP, + StaticMethodInvocation(insn, _, callee, caller). + + InstantiatedClass(insn, class) :- + Reachable(method, _), + AssignHeapAllocation(insn, _, heap, _, method, _), + NormalHeap(heap, class). + + Reachable(callee, n+1), + CallGraph(insn, caller, callee) :- + Reachable(caller, n), + !BanCaller(caller), + n < MAXSTEP, + VirtualMethodInvocation(insn, _, method, receiver, caller), + MethodInfo(method, simplename, _, _, _, descriptor, _), + VarType(receiver, class), + SubEqClass(subeqclass, class), + !ClassModifier("abstract", subeqclass), + InstantiatedClass(_, subeqclass), + Dispatch(simplename, descriptor, subeqclass, callee). + + Reachable(callee, n+1), + CallGraph(insn, caller, callee) :- + Reachable(caller, n), + !BanCaller(caller), + n < MAXSTEP, + StaticMethodInvocation(insn, _, method, caller), + MethodInfo(method, "doPrivileged", _, "java.security.AccessController", _, _, _), + ActualParam(0, insn, param), + VarType(param, class), + MethodInfo(callee, "run", _, class, _, _, 0). + + // RTAO is RTA OPTIMIZATION LEVEL + #if RTAO > 0 + + .decl SinkReachable(method:Method, sink:Method, step:number) + + SinkReachable(sink, sink, 0) :- + SinkMethod(sink). + + SinkReachable(caller, sink, n+1) :- + n < MAXSTEP, + SinkReachable(callee, sink, n), + CallGraph(_, caller, callee). + + #endif + + #if RTAO > 1 + + .decl ShortestPathToSink(caller:Method, sink:Method, step:number) + + ShortestPathToSink(entry, sink, n) :- + n = min step : {SinkReachable(entry, sink, step)}, + SinkMethod(sink), + EntryMethod(entry). + + ShortestPathToSink(callee, sink, n-1) :- + n < MAXSTEP + 1, + ShortestPathToSink(caller, sink, n), + SinkReachable(callee, sink, n-1), + CallGraph(_, caller, callee). + + #endif + + .decl RefinedReachable(method:Method) + + #if defined(RTAO) + #if RTAO == 1 + RefinedReachable(method) :- + SinkReachable(method, _, _). + #endif + #if RTAO == 2 + RefinedReachable(method) :- + ShortestPathToSink(method, _, _). + #endif + #else + RefinedReachable(method) :- + Reachable(method, _). + #endif + + .decl CallNode(node:Method, label:symbol) + .output CallNode + + CallNode(node, "method") :- + !EntryMethod(node), + !SinkMethod(node), + RefinedReachable(node). + + CallNode(node, "sink") :- + RefinedReachable(node), + SinkMethod(node). + + CallNode(node, "entry") :- + RefinedReachable(node), + EntryMethod(node). + + .decl CallEdge(caller:Method, callee:Method) + .output CallEdge + + CallEdge(caller, callee) :- + RefinedReachable(caller), + RefinedReachable(callee), + CallGraph(_, caller, callee). +} \ No newline at end of file diff --git a/neoImportCall.sh b/neoImportCall.sh index 8e1834a..da1e711 100644 --- a/neoImportCall.sh +++ b/neoImportCall.sh @@ -2,7 +2,7 @@ dbname=$1$(date "+%m%d%H%M") -neo4j-admin import --relationships=Call=/bytecodedl/neo4j/CallEdgeHeader.csv,/bytecodedl/output/CallEdge.csv --nodes=/bytecodedl/neo4j/CallNodeHeader.csv,/bytecodedl/output/CallNode.csv --database=$dbname --delimiter="\t" +neo4j-admin import --relationships=Call="/bytecodedl/neo4j/CallEdgeHeader.csv,/bytecodedl/output/.*CallEdge.csv" --nodes="/bytecodedl/neo4j/CallNodeHeader.csv,/bytecodedl/output/.*CallNode.csv" --database=$dbname --delimiter="\t" if grep -q "dbms.active_database" /var/lib/neo4j/conf/neo4j.conf; then sed -i -E "s/dbms.active_database=\w+/dbms.active_database=$dbname/g" /var/lib/neo4j/conf/neo4j.conf From b3a1e423ed03accc154506d515bce99d3d625775 Mon Sep 17 00:00:00 2001 From: yxxx Date: Sun, 24 Apr 2022 16:31:32 +0800 Subject: [PATCH 04/36] add rta --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 510251e..7c1ff31 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ you can use the docker we builded like docker-compose.yml - [x] 搜索功能 - [ ] 调用图分析 - [x] CHA - - [ ] RTA + - [x] RTA - [ ] 指针分析 - [x] 上下文无关指针分析 - [ ] 一阶上下文调用点敏感指针分析 From 963b04dbacef70daf8cff35615ced504710b7d90 Mon Sep 17 00:00:00 2001 From: yxxx Date: Mon, 25 Apr 2022 18:49:12 +0800 Subject: [PATCH 05/36] add logo and plugin --- README.md | 7 +++++++ bdl-logo.png | Bin 0 -> 59042 bytes 2 files changed, 7 insertions(+) create mode 100644 bdl-logo.png diff --git a/README.md b/README.md index 7c1ff31..ed49403 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # ByteCodeDL +![logo](./bdl-logo.png) + A declarative static analysis tool for jvm bytecode based Datalog like CodeQL ## Why ByteCodeDL @@ -39,6 +41,11 @@ you can use the docker we builded like docker-compose.yml 见docs文件夹 +## Plugin + +- IDEA + - [BDLH](https://github.com/BytecodeDL/BDLH) + ## Acknowledgement - 感谢南大的李樾和谭添两位老师,通过他们开设的[程序分析课程](https://pascal-group.bitbucket.io/teaching.html)入门了静态分析这一领域。 diff --git a/bdl-logo.png b/bdl-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..327431967316bf49644196bcf080d5627d18a870 GIT binary patch literal 59042 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 Date: Mon, 25 Apr 2022 18:53:34 +0800 Subject: [PATCH 06/36] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ed49403..cf3002d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # ByteCodeDL -![logo](./bdl-logo.png) +
+ ByteCodeDL +
A declarative static analysis tool for jvm bytecode based Datalog like CodeQL From 1a63d1fcb6d8cd44ab522bca67121074957cd8b2 Mon Sep 17 00:00:00 2001 From: yxxx Date: Thu, 28 Apr 2022 15:50:13 +0800 Subject: [PATCH 07/36] add call and node header --- .gitignore | 1 - neo4j/CallEdgeHeader.csv | 1 + neo4j/CallNodeHeader.csv | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 neo4j/CallEdgeHeader.csv create mode 100644 neo4j/CallNodeHeader.csv diff --git a/.gitignore b/.gitignore index 0fa412e..56b5210 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -*.csv output/*.csv .DS_Store .idea diff --git a/neo4j/CallEdgeHeader.csv b/neo4j/CallEdgeHeader.csv new file mode 100644 index 0000000..a163d36 --- /dev/null +++ b/neo4j/CallEdgeHeader.csv @@ -0,0 +1 @@ +:START_ID :END_ID \ No newline at end of file diff --git a/neo4j/CallNodeHeader.csv b/neo4j/CallNodeHeader.csv new file mode 100644 index 0000000..bff2ea0 --- /dev/null +++ b/neo4j/CallNodeHeader.csv @@ -0,0 +1 @@ +method:ID :LABEL \ No newline at end of file From 0fc41d2c144a630bc648bedcca7e2f24ba1711d7 Mon Sep 17 00:00:00 2001 From: yxxx Date: Fri, 29 Apr 2022 00:04:04 +0800 Subject: [PATCH 08/36] add datalog plugin --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cf3002d..2d23b55 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ you can use the docker we builded like docker-compose.yml ## Features - [x] 搜索功能 -- [ ] 调用图分析 +- [x] 调用图分析 - [x] CHA - [x] RTA - [ ] 指针分析 @@ -46,7 +46,8 @@ you can use the docker we builded like docker-compose.yml ## Plugin - IDEA - - [BDLH](https://github.com/BytecodeDL/BDLH) + - ByteCodeDL helper [BDLH](https://github.com/BytecodeDL/BDLH) + - Datalog language plugin [intellij-datalog](https://github.com/BytecodeDL/intellij-datalog) ## Acknowledgement From 57be83aac6d61ac3f0de018fa4ac901440ce2fc8 Mon Sep 17 00:00:00 2001 From: yxxx Date: Fri, 29 Apr 2022 18:29:42 +0800 Subject: [PATCH 09/36] include relative path instead include dir --- example/cha-example-1.dl | 4 +--- example/ctf-buggyLoader.dl | 4 +--- example/ctf-ezchain.dl | 4 +--- example/pt-noctx-example-1.dl | 4 +--- example/ptaint-example-1.dl | 4 +--- example/ptaint-example-2.dl | 4 +--- example/query-example-1.dl | 2 +- logic/cha.dl | 4 +++- logic/inputDeclaration.dl | 14 ++++++++------ logic/pt-noctx.dl | 3 +++ logic/rta.dl | 6 +++++- logic/utils.dl | 3 +++ 12 files changed, 29 insertions(+), 27 deletions(-) diff --git a/example/cha-example-1.dl b/example/cha-example-1.dl index 415df41..32beeb2 100644 --- a/example/cha-example-1.dl +++ b/example/cha-example-1.dl @@ -1,8 +1,6 @@ #define MAXSTEP 8 -#include "inputDeclaration.dl" -#include "utils.dl" -#include "cha.dl" +#include "../logic/cha.dl" // init entrypoint diff --git a/example/ctf-buggyLoader.dl b/example/ctf-buggyLoader.dl index f6052d9..266911a 100644 --- a/example/ctf-buggyLoader.dl +++ b/example/ctf-buggyLoader.dl @@ -1,9 +1,7 @@ #define MAXSTEP 5 #define CHAO 2 -#include "inputDeclaration.dl" -#include "utils.dl" -#include "cha.dl" +#include "../logic/cha.dl" .decl NonParamPublicMethod(method:Method, class:Class) diff --git a/example/ctf-ezchain.dl b/example/ctf-ezchain.dl index 59355fa..7a5be3d 100644 --- a/example/ctf-ezchain.dl +++ b/example/ctf-ezchain.dl @@ -1,9 +1,7 @@ #define MAXSTEP 5 #define CHAO 2 -#include "inputDeclaration.dl" -#include "utils.dl" -#include "cha.dl" +#include "../logic/cha.dl" .decl NonParamPublicMethod(method:Method, class:Class) diff --git a/example/pt-noctx-example-1.dl b/example/pt-noctx-example-1.dl index 665b49f..48ce998 100644 --- a/example/pt-noctx-example-1.dl +++ b/example/pt-noctx-example-1.dl @@ -1,6 +1,4 @@ -#include "inputDeclaration.dl" -#include "utils.dl" -#include "pt-noctx.dl" +#include "../logic/pt-noctx.dl" .init cipt = ContextInsensitivePt diff --git a/example/ptaint-example-1.dl b/example/ptaint-example-1.dl index 4ca9a2a..369f48b 100644 --- a/example/ptaint-example-1.dl +++ b/example/ptaint-example-1.dl @@ -1,6 +1,4 @@ -#include "inputDeclaration.dl" -#include "utils.dl" -#include "ptaint.dl" +#include "../logic/ptaint.dl" .init ptaint = PTaint diff --git a/example/ptaint-example-2.dl b/example/ptaint-example-2.dl index 8b8826e..5ca1171 100644 --- a/example/ptaint-example-2.dl +++ b/example/ptaint-example-2.dl @@ -1,6 +1,4 @@ -#include "inputDeclaration.dl" -#include "utils.dl" -#include "ptaint.dl" +#include "../logic/ptaint.dl" .init ptaint = PTaint diff --git a/example/query-example-1.dl b/example/query-example-1.dl index e7d5aa6..58a63a2 100644 --- a/example/query-example-1.dl +++ b/example/query-example-1.dl @@ -1,4 +1,4 @@ -#include "inputDeclaration.dl" +#include "../logic/inputDeclaration.dl" .decl QueryResult(class:Class, method:Method) .output QueryResult diff --git a/logic/cha.dl b/logic/cha.dl index 1e592fe..77387fb 100644 --- a/logic/cha.dl +++ b/logic/cha.dl @@ -1,3 +1,5 @@ +#pragma once +#include "utils.dl" .decl EntryPoint(simplename:symbol, descriptor:symbol, class:Class) .decl Reachable(method:Method, step:number) @@ -91,7 +93,7 @@ ShortestPathToSink(callee, sink, n-1) :- .decl RefinedReachable(method:Method) -#if defined(CHAO) +#ifdef CHAO #if CHAO == 1 RefinedReachable(method) :- SinkReachable(method, _, _). diff --git a/logic/inputDeclaration.dl b/logic/inputDeclaration.dl index 904d634..833ce6b 100644 --- a/logic/inputDeclaration.dl +++ b/logic/inputDeclaration.dl @@ -1,3 +1,5 @@ +#pragma once + .type Insn <: symbol .type Var <: symbol .type Heap <: symbol @@ -29,10 +31,10 @@ // method .decl MethodInfo(method:Method, simplename:symbol, param:symbol, class:Class, return:Class, jvmDescriptor:symbol, arity:number) -.input MethodInfo(IO="file", filename="Method.facts", delimiter="\t") +.input MethodInfo(IO=file, filename="Method.facts", delimiter="\t") .decl MethodModifier(mod:symbol, method:Method) -.input MethodModifier(filename="Method-Modifier.facts") +.input MethodModifier(IO=file, filename="Method-Modifier.facts", delimiter="\t") .decl ThisVar(method:Method, this:Var) .input ThisVar @@ -61,10 +63,10 @@ // Field .decl FieldInfo(field:Field, declaringType:Class, simplename:symbol, type:Class) -.input FieldInfo(IO="file", filename="Field.facts", delimiter="\t") +.input FieldInfo(IO=file, filename="Field.facts", delimiter="\t") .decl FieldModifier(modifier:symbol, field:Field) -.input FieldModifier(filename="Field-Modifier.facts") +.input FieldModifier(IO=file, filename="Field-Modifier.facts", delimiter="\t") .decl LoadInstanceField(insn:Insn, index:number, var:Var, base:Var, field:Field, inMethod:Method) .input LoadInstanceField @@ -90,13 +92,13 @@ // others .decl VarType(var:Var, class:Class) -.input VarType(IO="file", filename="Var-Type.facts", delimiter="\t") +.input VarType(IO=file, filename="Var-Type.facts", delimiter="\t") .decl AssignLocal(insn:Insn, index:number, from:Var, to:Var, inMethod: Method) .input AssignLocal .decl AssignCast(insn:Insn, index:number, from:Var, to:Var, type:Class, inMethod:Method) -.input AssignCast(filename="AssignCast.facts") +.input AssignCast(IO=file, filename="AssignCast.facts", delimiter="\t") .decl AssignHeapAllocation(insn:Insn, index:number, heap:Heap, var:Var, inMethod:Method, linenumber:number) .input AssignHeapAllocation diff --git a/logic/pt-noctx.dl b/logic/pt-noctx.dl index e662258..217a15f 100644 --- a/logic/pt-noctx.dl +++ b/logic/pt-noctx.dl @@ -1,3 +1,6 @@ +#pragma once +#include "utils.dl" + .comp ContextInsensitivePt{ .decl VarPointsTo(heap:Heap, var:Var) .decl InstanceFieldPointsTo(heap:Heap, baseHeap:Heap, field:Field) diff --git a/logic/rta.dl b/logic/rta.dl index f2720cb..deb2019 100644 --- a/logic/rta.dl +++ b/logic/rta.dl @@ -1,3 +1,7 @@ +#pragma once +#include "inputDeclaration.dl" +#include "utils.dl" + .comp RTA{ .decl EntryPoint(simplename:symbol, descriptor:symbol, class:Class) .decl Reachable(method:Method, step:number) @@ -97,7 +101,7 @@ .decl RefinedReachable(method:Method) - #if defined(RTAO) + #ifdef RTAO #if RTAO == 1 RefinedReachable(method) :- SinkReachable(method, _, _). diff --git a/logic/utils.dl b/logic/utils.dl index bb8a9b0..9bcc016 100644 --- a/logic/utils.dl +++ b/logic/utils.dl @@ -1,3 +1,6 @@ +#pragma once +#include "inputDeclaration.dl" + //self define relation // Utils From c564ec4f68218e83461e759bde501f4333c92a58 Mon Sep 17 00:00:00 2001 From: yxxx Date: Fri, 29 Apr 2022 18:43:33 +0800 Subject: [PATCH 10/36] include relative path instead include dir --- logic/ptaint.dl | 1 + 1 file changed, 1 insertion(+) diff --git a/logic/ptaint.dl b/logic/ptaint.dl index d9043de..0c88847 100644 --- a/logic/ptaint.dl +++ b/logic/ptaint.dl @@ -1,3 +1,4 @@ +#pragma once #include "pt-noctx.dl" .comp PTaint{ From b718f0d99dfca12d0283d8e48655ee674720537f Mon Sep 17 00:00:00 2001 From: yxxx Date: Fri, 29 Apr 2022 18:51:38 +0800 Subject: [PATCH 11/36] add rta example --- example/rta-example-1.dl | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 example/rta-example-1.dl diff --git a/example/rta-example-1.dl b/example/rta-example-1.dl new file mode 100644 index 0000000..8b80869 --- /dev/null +++ b/example/rta-example-1.dl @@ -0,0 +1,7 @@ +#define MAXSTEP 8 + +#include "../logic/RTA.dl" + +.init rta = RTA + +rta.EntryPoint("main", "([Ljava/lang/String;)V", "com.bytecodedl.benchmark.demo.VirtualCallDemo1"). \ No newline at end of file From 3f6fbdfc9aaa2903b685f1fd45ae0bc6bcb71c7c Mon Sep 17 00:00:00 2001 From: yxxx Date: Fri, 29 Apr 2022 19:02:05 +0800 Subject: [PATCH 12/36] update docs --- docs/callgraph.md | 2 +- docs/context-insensitive-points-to.md | 2 +- docs/ptaint.md | 2 +- docs/readme.md | 4 ++++ docs/utils.md | 2 +- 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/callgraph.md b/docs/callgraph.md index 38f7d74..5183f9b 100644 --- a/docs/callgraph.md +++ b/docs/callgraph.md @@ -140,7 +140,7 @@ CHA只会存在误报,但是RTA既可能存在误报也可能存在漏报。 1. 执行下面命令将结果输出到output文件夹 ```bash - souffle -I ~/code/ByteCodeDL/logic -F factsdir -D ~/code/ByteCodeDL/output ~/code/ByteCodeDL/example/cha-example-1.dl + souffle -F factsdir -D ~/code/ByteCodeDL/output ~/code/ByteCodeDL/example/cha-example-1.dl ``` 2. 执行bash importOutput2Neo4j.sh neoImportCall.sh dbname diff --git a/docs/context-insensitive-points-to.md b/docs/context-insensitive-points-to.md index 848eec1..5aa8ecf 100644 --- a/docs/context-insensitive-points-to.md +++ b/docs/context-insensitive-points-to.md @@ -263,7 +263,7 @@ cipt.Reachable(method) :- 然后执行 ```bash -souffle -I ByteCodeDL/logic -F factsdir -D ByteCodeDL/output ByteCodeDL/example/pt-noctx-example-1.dl +souffle -F factsdir -D ByteCodeDL/output ByteCodeDL/example/pt-noctx-example-1.dl ``` 分析结果保存在 output/cipt.VarPointsTo diff --git a/docs/ptaint.md b/docs/ptaint.md index 469ab5a..654dcb9 100644 --- a/docs/ptaint.md +++ b/docs/ptaint.md @@ -375,7 +375,7 @@ java8 -jar ~/code/soot-fact-generator/build/libs/soot-fact-generator.jar -i Benc // 切换目录 cd tainttest // 执行souffle -souffle -I ~/code/ByteCodeDL/logic -F . -D output ~/code/ByteCodeDL/example/ptaint-example-1.dl +souffle -F . -D output ~/code/ByteCodeDL/example/ptaint-example-1.dl ``` 然后在ouput目录能够看到`grep "Demo3" TaintVar.csv`结果 diff --git a/docs/readme.md b/docs/readme.md index ea6a55e..9f66840 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -10,3 +10,7 @@ 3. [utils.md](utils.md) 4. [query.md](query.md) 5. [callgraph.md](callgraph.md) +6. [cha-optimization.md](cha-optimization.md) +7. [cha-in-ctf.md](cha-in-ctf.md) +8. [context-insensitive-points-to.md](context-insensitive-points-to.md) +9. [ptaint](ptaint.md) diff --git a/docs/utils.md b/docs/utils.md index b78025d..3162464 100644 --- a/docs/utils.md +++ b/docs/utils.md @@ -1,6 +1,6 @@ # utils -## Class Hierachy +## Class Hierarchy 需要构建一个类型层次图,用于寻找某个类的子类、父类,或者用于判断两个类之间是否有继承关系。 From 1ee8174463e237f5747150e6644ab2b3355e5386 Mon Sep 17 00:00:00 2001 From: yxxx Date: Sun, 1 May 2022 19:27:15 +0800 Subject: [PATCH 13/36] add one callsite context sensitive points-to --- .../one-callsite-sensitive-pt-example-1.dl | 10 ++ logic/inputDeclaration.dl | 1 + logic/one-callsite-sensitive-pt.dl | 118 ++++++++++++++++++ 3 files changed, 129 insertions(+) create mode 100644 example/one-callsite-sensitive-pt-example-1.dl create mode 100644 logic/one-callsite-sensitive-pt.dl diff --git a/example/one-callsite-sensitive-pt-example-1.dl b/example/one-callsite-sensitive-pt-example-1.dl new file mode 100644 index 0000000..d640850 --- /dev/null +++ b/example/one-callsite-sensitive-pt-example-1.dl @@ -0,0 +1,10 @@ +#define MAXSTEP 8 +#include "../logic/one-callsite-sensitive-pt.dl" + +.init callsitecsDemo1 = OneCallsiteSensitivePT +callsitecsDemo1.Reachable("", "initCtx", 0). +.output callsitecsDemo1.VarPointsTo + +.init callsitecsDemo2 = OneCallsiteSensitivePT +callsitecsDemo2.Reachable("", "initCtx", 0). +.output callsitecsDemo2.VarPointsTo \ No newline at end of file diff --git a/logic/inputDeclaration.dl b/logic/inputDeclaration.dl index 833ce6b..a8fcf28 100644 --- a/logic/inputDeclaration.dl +++ b/logic/inputDeclaration.dl @@ -6,6 +6,7 @@ .type Field <: symbol .type Method <: symbol .type Class <: symbol +.type Context = symbol // load data from facts // class diff --git a/logic/one-callsite-sensitive-pt.dl b/logic/one-callsite-sensitive-pt.dl new file mode 100644 index 0000000..33822b9 --- /dev/null +++ b/logic/one-callsite-sensitive-pt.dl @@ -0,0 +1,118 @@ +#pragma once +#include "utils.dl" + +.comp OneCallsiteSensitivePT{ + .decl VarPointsTo(heap:Heap, hctx:Context, var:Var, vCtx:Context) + .decl InstanceFieldPointsTo(heap:Heap, hctx:Context, baseHeap:Heap, bhCtx:Context, field:Field) + .decl StaticFieldPointsTo(heap:Heap, hctx:Context, field:Field) + .decl ArrayIndexPointsTo(heap:Heap, hctx:Context, baseHeap:Heap, bhCtx:Context) + .decl Reachable(method:Method, ctx:Context, n:number) + .decl CallGraph(insn:Insn, caller:Method, callerCtx:Context, callee:Method, calleeCtx:Context) + + // new + VarPointsTo(heap, ctx, var, ctx) :- + Reachable(method, ctx, _), + AssignHeapAllocation(_, _, heap, var, method, _). + + // assign + VarPointsTo(heap, hctx, to, ctx) :- + Reachable(method, ctx, _), + VarPointsTo(heap, hctx, from, ctx), + AssignLocal(_, _, from, to, method). + + // cast + VarPointsTo(heap, hctx, to, ctx) :- + Reachable(method, ctx, _), + AssignCast(_, _, from, to, _, method), + VarPointsTo(heap, hctx, from, ctx). + + // load field + VarPointsTo(heap, hctx, to, ctx) :- + Reachable(method, ctx, _), + LoadInstanceField(_, _, to, base, field, method), + VarPointsTo(baseHeap, bhCtx, base, ctx), + InstanceFieldPointsTo(heap, hctx, baseHeap, bhCtx, field). + + // store field + InstanceFieldPointsTo(heap, hctx, baseHeap, bhCtx, field) :- + Reachable(method, ctx, _), + StoreInstanceField(_, _, from, base, field, method), + VarPointsTo(heap, hctx, from, ctx), + VarPointsTo(baseHeap, bhCtx, base, ctx). + + // load staic field + VarPointsTo(heap, hctx, to, ctx) :- + Reachable(method, ctx, _), + LoadStaticField(_, _, to, field, method), + StaticFieldPointsTo(heap, hctx, field). + + // store static field + StaticFieldPointsTo(heap, hctx, field) :- + Reachable(method, ctx, _), + StoreStaticField(_, _, from, field, method), + VarPointsTo(heap, hctx, from, ctx). + + // load from array + VarPointsTo(heap, hctx, to, ctx) :- + Reachable(method, ctx, _), + LoadArrayIndex(_, _, to, base, method), + VarPointsTo(baseHeap, bhCtx, base, ctx), + ArrayIndexPointsTo(heap, hctx, baseHeap, bhCtx). + + // store into array + ArrayIndexPointsTo(heap, hctx, baseHeap, bhCtx) :- + Reachable(method, ctx, _), + StoreArrayIndex(_, _, from, base, method), + VarPointsTo(heap, hctx, from, ctx), + VarPointsTo(baseHeap, bhCtx, base, ctx). + + Reachable(callee, calleeCtx, n+1), + CallGraph(insn, caller, callerCtx, callee, calleeCtx) :- + Reachable(caller, callerCtx, n), + n < MAXSTEP, + calleeCtx = insn, + SpecialMethodInvocation(insn, _, callee, _, caller). + + Reachable(callee, calleeCtx, n+1), + CallGraph(insn, caller, callerCtx, callee, calleeCtx) :- + Reachable(caller, callerCtx, n), + n < MAXSTEP, + calleeCtx = insn, + StaticMethodInvocation(insn, _, callee, caller). + + Reachable(callee, calleeCtx, n+1), + CallGraph(insn, caller, callerCtx, callee, calleeCtx) :- + Reachable(caller, callerCtx, n), + n < MAXSTEP, + VirtualMethodInvocation(insn, _, method, base, caller), + calleeCtx = insn, + VarPointsTo(baseHeap, _, base, callerCtx), + NormalHeap(baseHeap, class), + MethodInfo(method, simplename, _, _, _, descriptor, _), + Dispatch(simplename, descriptor, class, callee). + + // param + VarPointsTo(heap, hctx, param, calleeCtx) :- + CallGraph(insn, _, callerCtx, callee, calleeCtx), + ActualParam(n, insn, arg), + FormalParam(n, callee, param), + VarPointsTo(heap, hctx, arg, callerCtx), + NormalHeap(heap, _). + + // return + VarPointsTo(heap, hctx, return, callerCtx) :- + CallGraph(insn, _, callerCtx, callee, calleeCtx), + Return(_, _, var, callee), + AssignReturnValue(insn, return), + VarPointsTo(heap, hctx, var, calleeCtx). + + // this + VarPointsTo(heap, hctx, this, calleeCtx) :- + CallGraph(insn, _, callerCtx, callee, calleeCtx), + ( + VirtualMethodInvocation(insn, _, _, base, _); + SpecialMethodInvocation(insn, _, _, base, _) + ), + ThisVar(callee, this), + VarPointsTo(heap, hctx, base, callerCtx). +} \ No newline at end of file From 19e063171cefb0be52acff458ae6bd527cbbf4c1 Mon Sep 17 00:00:00 2001 From: yxxx Date: Sun, 1 May 2022 19:27:51 +0800 Subject: [PATCH 14/36] add one object context sensitive points-to --- example/one-object-sensitive-pt-example-1.dl | 10 ++ logic/one-object-sensitive-pt.dl | 119 +++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 example/one-object-sensitive-pt-example-1.dl create mode 100644 logic/one-object-sensitive-pt.dl diff --git a/example/one-object-sensitive-pt-example-1.dl b/example/one-object-sensitive-pt-example-1.dl new file mode 100644 index 0000000..69f37d1 --- /dev/null +++ b/example/one-object-sensitive-pt-example-1.dl @@ -0,0 +1,10 @@ +#define MAXSTEP 8 +#include "../logic/one-object-sensitive-pt.dl" + +.init objectDemo1 = OneObjectSensitivePT +objectDemo1.Reachable("", "initCtx", 0). +.output objectDemo1.VarPointsTo + +.init objectDemo2 = OneObjectSensitivePT +objectDemo2.Reachable("", "initCtx", 0). +.output objectDemo2.VarPointsTo \ No newline at end of file diff --git a/logic/one-object-sensitive-pt.dl b/logic/one-object-sensitive-pt.dl new file mode 100644 index 0000000..47b1363 --- /dev/null +++ b/logic/one-object-sensitive-pt.dl @@ -0,0 +1,119 @@ +#pragma once +#include "utils.dl" + +.comp OneObjectSensitivePT{ + .decl VarPointsTo(heap:Heap, hctx:Context, var:Var, vCtx:Context) + .decl InstanceFieldPointsTo(heap:Heap, hctx:Context, baseHeap:Heap, bhCtx:Context, field:Field) + .decl StaticFieldPointsTo(heap:Heap, hctx:Context, field:Field) + .decl ArrayIndexPointsTo(heap:Heap, hctx:Context, baseHeap:Heap, bhCtx:Context) + .decl Reachable(method:Method, ctx:Context, n:number) + .decl CallGraph(insn:Insn, caller:Method, callerCtx:Context, callee:Method, calleeCtx:Context) + + // new + VarPointsTo(heap, ctx, var, ctx) :- + Reachable(method, ctx, _), + AssignHeapAllocation(_, _, heap, var, method, _). + + // assign + VarPointsTo(heap, hctx, to, ctx) :- + Reachable(method, ctx, _), + VarPointsTo(heap, hctx, from, ctx), + AssignLocal(_, _, from, to, method). + + // cast + VarPointsTo(heap, hctx, to, ctx) :- + Reachable(method, ctx, _), + AssignCast(_, _, from, to, _, method), + VarPointsTo(heap, hctx, from, ctx). + + // load field + VarPointsTo(heap, hctx, to, ctx) :- + Reachable(method, ctx, _), + LoadInstanceField(_, _, to, base, field, method), + VarPointsTo(baseHeap, bhCtx, base, ctx), + InstanceFieldPointsTo(heap, hctx, baseHeap, bhCtx, field). + + // store field + InstanceFieldPointsTo(heap, hctx, baseHeap, bhCtx, field) :- + Reachable(method, ctx, _), + StoreInstanceField(_, _, from, base, field, method), + VarPointsTo(heap, hctx, from, ctx), + VarPointsTo(baseHeap, bhCtx, base, ctx). + + // load staic field + VarPointsTo(heap, hctx, to, ctx) :- + Reachable(method, ctx, _), + LoadStaticField(_, _, to, field, method), + StaticFieldPointsTo(heap, hctx, field). + + // store static field + StaticFieldPointsTo(heap, hctx, field) :- + Reachable(method, ctx, _), + StoreStaticField(_, _, from, field, method), + VarPointsTo(heap, hctx, from, ctx). + + // load from array + VarPointsTo(heap, hctx, to, ctx) :- + Reachable(method, ctx, _), + LoadArrayIndex(_, _, to, base, method), + VarPointsTo(baseHeap, bhCtx, base, ctx), + ArrayIndexPointsTo(heap, hctx, baseHeap, bhCtx). + + // store into array + ArrayIndexPointsTo(heap, hctx, baseHeap, bhCtx) :- + Reachable(method, ctx, _), + StoreArrayIndex(_, _, from, base, method), + VarPointsTo(heap, hctx, from, ctx), + VarPointsTo(baseHeap, bhCtx, base, ctx). + + Reachable(callee, calleeCtx, n+1), + CallGraph(insn, caller, callerCtx, callee, calleeCtx) :- + Reachable(caller, callerCtx, n), + n < MAXSTEP, + SpecialMethodInvocation(insn, _, callee, base, caller), + VarPointsTo(baseHeap, _, base, callerCtx), + calleeCtx = baseHeap. + + Reachable(callee, calleeCtx, n+1), + CallGraph(insn, caller, callerCtx, callee, calleeCtx) :- + Reachable(caller, callerCtx, n), + n < MAXSTEP, + StaticMethodInvocation(insn, _, callee, caller), + calleeCtx = insn. // maybe have bug + + Reachable(callee, calleeCtx, n+1), + CallGraph(insn, caller, callerCtx, callee, calleeCtx) :- + Reachable(caller, callerCtx, n), + n < MAXSTEP, + VirtualMethodInvocation(insn, _, method, base, caller), + VarPointsTo(baseHeap, _, base, callerCtx), + NormalHeap(baseHeap, class), + MethodInfo(method, simplename, _, _, _, descriptor, _), + Dispatch(simplename, descriptor, class, callee), + calleeCtx = baseHeap. + + // param + VarPointsTo(heap, hctx, param, calleeCtx) :- + CallGraph(insn, _, callerCtx, callee, calleeCtx), + ActualParam(n, insn, arg), + FormalParam(n, callee, param), + VarPointsTo(heap, hctx, arg, callerCtx), + NormalHeap(heap, _). + + // return + VarPointsTo(heap, hctx, return, callerCtx) :- + CallGraph(insn, _, callerCtx, callee, calleeCtx), + Return(_, _, var, callee), + AssignReturnValue(insn, return), + VarPointsTo(heap, hctx, var, calleeCtx). + + // this + VarPointsTo(heap, hctx, this, calleeCtx) :- + CallGraph(insn, _, callerCtx, callee, calleeCtx), + ( + VirtualMethodInvocation(insn, _, _, base, _); + SpecialMethodInvocation(insn, _, _, base, _) + ), + ThisVar(callee, this), + VarPointsTo(heap, hctx, base, callerCtx). +} \ No newline at end of file From b662b1e7e615b887dc676a8f7eafffb6632e2cb7 Mon Sep 17 00:00:00 2001 From: yxxx Date: Sun, 1 May 2022 19:29:17 +0800 Subject: [PATCH 15/36] add one type context sensitive point-to --- example/one-type-sensitive-pt-example-1.dl | 10 ++ logic/one-type-sensitive-pt.dl | 123 +++++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 example/one-type-sensitive-pt-example-1.dl create mode 100644 logic/one-type-sensitive-pt.dl diff --git a/example/one-type-sensitive-pt-example-1.dl b/example/one-type-sensitive-pt-example-1.dl new file mode 100644 index 0000000..741891e --- /dev/null +++ b/example/one-type-sensitive-pt-example-1.dl @@ -0,0 +1,10 @@ +#define MAXSTEP 8 +#include "../logic/one-type-sensitive-pt.dl" + +.init typeDemo1 = OneTypeSensitivePT +typeDemo1.Reachable("", "initCtx", 0). +.output typeDemo1.VarPointsTo + +.init typeDemo2 = OneTypeSensitivePT +typeDemo2.Reachable("", "initCtx", 0). +.output typeDemo2.VarPointsTo \ No newline at end of file diff --git a/logic/one-type-sensitive-pt.dl b/logic/one-type-sensitive-pt.dl new file mode 100644 index 0000000..bdf9efc --- /dev/null +++ b/logic/one-type-sensitive-pt.dl @@ -0,0 +1,123 @@ +#pragma once +#include "utils.dl" + +.comp OneTypeSensitivePT{ + .decl VarPointsTo(heap:Heap, hctx:Context, var:Var, vCtx:Context) + .decl InstanceFieldPointsTo(heap:Heap, hctx:Context, baseHeap:Heap, bhCtx:Context, field:Field) + .decl StaticFieldPointsTo(heap:Heap, hctx:Context, field:Field) + .decl ArrayIndexPointsTo(heap:Heap, hctx:Context, baseHeap:Heap, bhCtx:Context) + .decl Reachable(method:Method, ctx:Context, n:number) + .decl CallGraph(insn:Insn, caller:Method, callerCtx:Context, callee:Method, calleeCtx:Context) + + // new + VarPointsTo(heap, ctx, var, ctx) :- + Reachable(method, ctx, _), + AssignHeapAllocation(_, _, heap, var, method, _). + + // assign + VarPointsTo(heap, hctx, to, ctx) :- + Reachable(method, ctx, _), + VarPointsTo(heap, hctx, from, ctx), + AssignLocal(_, _, from, to, method). + + // cast + VarPointsTo(heap, hctx, to, ctx) :- + Reachable(method, ctx, _), + AssignCast(_, _, from, to, _, method), + VarPointsTo(heap, hctx, from, ctx). + + // load field + VarPointsTo(heap, hctx, to, ctx) :- + Reachable(method, ctx, _), + LoadInstanceField(_, _, to, base, field, method), + VarPointsTo(baseHeap, bhCtx, base, ctx), + InstanceFieldPointsTo(heap, hctx, baseHeap, bhCtx, field). + + // store field + InstanceFieldPointsTo(heap, hctx, baseHeap, bhCtx, field) :- + Reachable(method, ctx, _), + StoreInstanceField(_, _, from, base, field, method), + VarPointsTo(heap, hctx, from, ctx), + VarPointsTo(baseHeap, bhCtx, base, ctx). + + // load staic field + VarPointsTo(heap, hctx, to, ctx) :- + Reachable(method, ctx, _), + LoadStaticField(_, _, to, field, method), + StaticFieldPointsTo(heap, hctx, field). + + // store static field + StaticFieldPointsTo(heap, hctx, field) :- + Reachable(method, ctx, _), + StoreStaticField(_, _, from, field, method), + VarPointsTo(heap, hctx, from, ctx). + + // load from array + VarPointsTo(heap, hctx, to, ctx) :- + Reachable(method, ctx, _), + LoadArrayIndex(_, _, to, base, method), + VarPointsTo(baseHeap, bhCtx, base, ctx), + ArrayIndexPointsTo(heap, hctx, baseHeap, bhCtx). + + // store into array + ArrayIndexPointsTo(heap, hctx, baseHeap, bhCtx) :- + Reachable(method, ctx, _), + StoreArrayIndex(_, _, from, base, method), + VarPointsTo(heap, hctx, from, ctx), + VarPointsTo(baseHeap, bhCtx, base, ctx). + + Reachable(callee, calleeCtx, n+1), + CallGraph(insn, caller, callerCtx, callee, calleeCtx) :- + Reachable(caller, callerCtx, n), + n < MAXSTEP, + SpecialMethodInvocation(insn, _, callee, base, caller), + VarPointsTo(baseHeap, _, base, callerCtx), + AssignHeapAllocation(_, _, baseHeap, _, inmethod, _), + MethodInfo(inmethod, _, _, inType, _, _, _), + calleeCtx = inType. + + Reachable(callee, calleeCtx, n+1), + CallGraph(insn, caller, callerCtx, callee, calleeCtx) :- + Reachable(caller, callerCtx, n), + n < MAXSTEP, + StaticMethodInvocation(insn, _, callee, caller), + calleeCtx = insn. // maybe have bug + + Reachable(callee, calleeCtx, n+1), + CallGraph(insn, caller, callerCtx, callee, calleeCtx) :- + Reachable(caller, callerCtx, n), + n < MAXSTEP, + VirtualMethodInvocation(insn, _, method, base, caller), + VarPointsTo(baseHeap, _, base, callerCtx), + NormalHeap(baseHeap, class), + MethodInfo(method, simplename, _, _, _, descriptor, _), + Dispatch(simplename, descriptor, class, callee), + AssignHeapAllocation(_, _, baseHeap, _, inmethod, _), + MethodInfo(inmethod, _, _, inType, _, _, _), + calleeCtx = inType. + + // param + VarPointsTo(heap, hctx, param, calleeCtx) :- + CallGraph(insn, _, callerCtx, callee, calleeCtx), + ActualParam(n, insn, arg), + FormalParam(n, callee, param), + VarPointsTo(heap, hctx, arg, callerCtx), + NormalHeap(heap, _). + + // return + VarPointsTo(heap, hctx, return, callerCtx) :- + CallGraph(insn, _, callerCtx, callee, calleeCtx), + Return(_, _, var, callee), + AssignReturnValue(insn, return), + VarPointsTo(heap, hctx, var, calleeCtx). + + // this + VarPointsTo(heap, hctx, this, calleeCtx) :- + CallGraph(insn, _, callerCtx, callee, calleeCtx), + ( + VirtualMethodInvocation(insn, _, _, base, _); + SpecialMethodInvocation(insn, _, _, base, _) + ), + ThisVar(callee, this), + VarPointsTo(heap, hctx, base, callerCtx). +} \ No newline at end of file From 4471af9c5fc2bc9be0dae61a4c32215821712bd2 Mon Sep 17 00:00:00 2001 From: yxxx Date: Sun, 1 May 2022 19:35:08 +0800 Subject: [PATCH 16/36] update readme --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2d23b55..e043fa5 100644 --- a/README.md +++ b/README.md @@ -33,11 +33,13 @@ you can use the docker we builded like docker-compose.yml - [x] RTA - [ ] 指针分析 - [x] 上下文无关指针分析 - - [ ] 一阶上下文调用点敏感指针分析 - - [ ] 一阶上下文对象敏感指针分析 - - [ ] 一阶上下文类型敏感指针分析 + - [x] 一阶上下文调用点敏感指针分析 + - [x] 一阶上下文对象敏感指针分析 + - [x] 一阶上下文类型敏感指针分析 + - [ ] 可选择上下文敏感指针分析 - [ ] 污点分析 - [x] 上下文无关ptaint + - [ ] 上下文敏感ptaint ## Usage From b711de0c8b962ac455af4b26771c4b47b3bff6c6 Mon Sep 17 00:00:00 2001 From: yxxx Date: Sun, 1 May 2022 22:39:05 +0800 Subject: [PATCH 17/36] refactor one callsite sensitive points-to --- .../one-callsite-sensitive-pt-example-1.dl | 4 +- logic/inputDeclaration.dl | 1 - logic/one-callsite-sensitive-pt.dl | 128 +++--------------- 3 files changed, 18 insertions(+), 115 deletions(-) diff --git a/example/one-callsite-sensitive-pt-example-1.dl b/example/one-callsite-sensitive-pt-example-1.dl index d640850..0a07ead 100644 --- a/example/one-callsite-sensitive-pt-example-1.dl +++ b/example/one-callsite-sensitive-pt-example-1.dl @@ -1,10 +1,10 @@ #define MAXSTEP 8 #include "../logic/one-callsite-sensitive-pt.dl" -.init callsitecsDemo1 = OneCallsiteSensitivePT +.init callsitecsDemo1 = OneCallsiteSensitivePT callsitecsDemo1.Reachable("", "initCtx", 0). .output callsitecsDemo1.VarPointsTo -.init callsitecsDemo2 = OneCallsiteSensitivePT +.init callsitecsDemo2 = OneCallsiteSensitivePT callsitecsDemo2.Reachable("", "initCtx", 0). .output callsitecsDemo2.VarPointsTo \ No newline at end of file diff --git a/logic/inputDeclaration.dl b/logic/inputDeclaration.dl index a8fcf28..833ce6b 100644 --- a/logic/inputDeclaration.dl +++ b/logic/inputDeclaration.dl @@ -6,7 +6,6 @@ .type Field <: symbol .type Method <: symbol .type Class <: symbol -.type Context = symbol // load data from facts // class diff --git a/logic/one-callsite-sensitive-pt.dl b/logic/one-callsite-sensitive-pt.dl index 33822b9..253c6c2 100644 --- a/logic/one-callsite-sensitive-pt.dl +++ b/logic/one-callsite-sensitive-pt.dl @@ -1,118 +1,22 @@ #pragma once -#include "utils.dl" +#include "abstract-context-sensitive-pt.dl" -.comp OneCallsiteSensitivePT{ - .decl VarPointsTo(heap:Heap, hctx:Context, var:Var, vCtx:Context) - .decl InstanceFieldPointsTo(heap:Heap, hctx:Context, baseHeap:Heap, bhCtx:Context, field:Field) - .decl StaticFieldPointsTo(heap:Heap, hctx:Context, field:Field) - .decl ArrayIndexPointsTo(heap:Heap, hctx:Context, baseHeap:Heap, bhCtx:Context) - .decl Reachable(method:Method, ctx:Context, n:number) - .decl CallGraph(insn:Insn, caller:Method, callerCtx:Context, callee:Method, calleeCtx:Context) +.type HContext = Insn +.type Context = Insn - // new - VarPointsTo(heap, ctx, var, ctx) :- - Reachable(method, ctx, _), - AssignHeapAllocation(_, _, heap, var, method, _). - - // assign - VarPointsTo(heap, hctx, to, ctx) :- - Reachable(method, ctx, _), - VarPointsTo(heap, hctx, from, ctx), - AssignLocal(_, _, from, to, method). - - // cast - VarPointsTo(heap, hctx, to, ctx) :- - Reachable(method, ctx, _), - AssignCast(_, _, from, to, _, method), - VarPointsTo(heap, hctx, from, ctx). - - // load field - VarPointsTo(heap, hctx, to, ctx) :- - Reachable(method, ctx, _), - LoadInstanceField(_, _, to, base, field, method), - VarPointsTo(baseHeap, bhCtx, base, ctx), - InstanceFieldPointsTo(heap, hctx, baseHeap, bhCtx, field). - - // store field - InstanceFieldPointsTo(heap, hctx, baseHeap, bhCtx, field) :- - Reachable(method, ctx, _), - StoreInstanceField(_, _, from, base, field, method), - VarPointsTo(heap, hctx, from, ctx), - VarPointsTo(baseHeap, bhCtx, base, ctx). - - // load staic field - VarPointsTo(heap, hctx, to, ctx) :- - Reachable(method, ctx, _), - LoadStaticField(_, _, to, field, method), - StaticFieldPointsTo(heap, hctx, field). - - // store static field - StaticFieldPointsTo(heap, hctx, field) :- - Reachable(method, ctx, _), - StoreStaticField(_, _, from, field, method), - VarPointsTo(heap, hctx, from, ctx). - - // load from array - VarPointsTo(heap, hctx, to, ctx) :- - Reachable(method, ctx, _), - LoadArrayIndex(_, _, to, base, method), - VarPointsTo(baseHeap, bhCtx, base, ctx), - ArrayIndexPointsTo(heap, hctx, baseHeap, bhCtx). - - // store into array - ArrayIndexPointsTo(heap, hctx, baseHeap, bhCtx) :- - Reachable(method, ctx, _), - StoreArrayIndex(_, _, from, base, method), - VarPointsTo(heap, hctx, from, ctx), - VarPointsTo(baseHeap, bhCtx, base, ctx). - - Reachable(callee, calleeCtx, n+1), - CallGraph(insn, caller, callerCtx, callee, calleeCtx) :- - Reachable(caller, callerCtx, n), - n < MAXSTEP, - calleeCtx = insn, - SpecialMethodInvocation(insn, _, callee, _, caller). - - Reachable(callee, calleeCtx, n+1), - CallGraph(insn, caller, callerCtx, callee, calleeCtx) :- - Reachable(caller, callerCtx, n), - n < MAXSTEP, - calleeCtx = insn, - StaticMethodInvocation(insn, _, callee, caller). - - Reachable(callee, calleeCtx, n+1), - CallGraph(insn, caller, callerCtx, callee, calleeCtx) :- - Reachable(caller, callerCtx, n), - n < MAXSTEP, - VirtualMethodInvocation(insn, _, method, base, caller), - calleeCtx = insn, - VarPointsTo(baseHeap, _, base, callerCtx), - NormalHeap(baseHeap, class), - MethodInfo(method, simplename, _, _, _, descriptor, _), - Dispatch(simplename, descriptor, class, callee). - - // param - VarPointsTo(heap, hctx, param, calleeCtx) :- - CallGraph(insn, _, callerCtx, callee, calleeCtx), - ActualParam(n, insn, arg), - FormalParam(n, callee, param), - VarPointsTo(heap, hctx, arg, callerCtx), - NormalHeap(heap, _). - - // return - VarPointsTo(heap, hctx, return, callerCtx) :- - CallGraph(insn, _, callerCtx, callee, calleeCtx), - Return(_, _, var, callee), - AssignReturnValue(insn, return), - VarPointsTo(heap, hctx, var, calleeCtx). - - // this - VarPointsTo(heap, hctx, this, calleeCtx) :- - CallGraph(insn, _, callerCtx, callee, calleeCtx), +.comp OneCallsiteSensitivePT : AbstractContextSensitivePT{ + .override SelectInvocationContext + SelectInvocationContext(callerCtx, insn, baseHeap, hctx, calleeCtx) :- + Reachable(caller, callerCtx, _), ( - VirtualMethodInvocation(insn, _, _, base, _); - SpecialMethodInvocation(insn, _, _, base, _) + SpecialMethodInvocation(insn, _, _, base, caller); + VirtualMethodInvocation(insn, _, _, base, caller) ), - ThisVar(callee, this), - VarPointsTo(heap, hctx, base, callerCtx). + VarPointsTo(baseHeap, hctx, base, callerCtx), + calleeCtx = insn. + + SelectStaticInvocationContext(callerCtx, insn, calleeCtx) :- + Reachable(caller, callerCtx, _), + StaticMethodInvocation(insn, _, _, caller), + calleeCtx = insn. } \ No newline at end of file From 18cecc4c1a387d9bfc549c73b1d1f33d95ca55f1 Mon Sep 17 00:00:00 2001 From: yxxx Date: Sun, 1 May 2022 22:39:25 +0800 Subject: [PATCH 18/36] refactor one object sensitive points-to --- example/one-object-sensitive-pt-example-1.dl | 4 +- logic/one-object-sensitive-pt.dl | 124 ++----------------- 2 files changed, 13 insertions(+), 115 deletions(-) diff --git a/example/one-object-sensitive-pt-example-1.dl b/example/one-object-sensitive-pt-example-1.dl index 69f37d1..becfa38 100644 --- a/example/one-object-sensitive-pt-example-1.dl +++ b/example/one-object-sensitive-pt-example-1.dl @@ -1,10 +1,10 @@ #define MAXSTEP 8 #include "../logic/one-object-sensitive-pt.dl" -.init objectDemo1 = OneObjectSensitivePT +.init objectDemo1 = OneObjectSensitivePT objectDemo1.Reachable("", "initCtx", 0). .output objectDemo1.VarPointsTo -.init objectDemo2 = OneObjectSensitivePT +.init objectDemo2 = OneObjectSensitivePT objectDemo2.Reachable("", "initCtx", 0). .output objectDemo2.VarPointsTo \ No newline at end of file diff --git a/logic/one-object-sensitive-pt.dl b/logic/one-object-sensitive-pt.dl index 47b1363..7ca1f24 100644 --- a/logic/one-object-sensitive-pt.dl +++ b/logic/one-object-sensitive-pt.dl @@ -1,119 +1,17 @@ #pragma once -#include "utils.dl" +#include "abstract-context-sensitive-pt.dl" -.comp OneObjectSensitivePT{ - .decl VarPointsTo(heap:Heap, hctx:Context, var:Var, vCtx:Context) - .decl InstanceFieldPointsTo(heap:Heap, hctx:Context, baseHeap:Heap, bhCtx:Context, field:Field) - .decl StaticFieldPointsTo(heap:Heap, hctx:Context, field:Field) - .decl ArrayIndexPointsTo(heap:Heap, hctx:Context, baseHeap:Heap, bhCtx:Context) - .decl Reachable(method:Method, ctx:Context, n:number) - .decl CallGraph(insn:Insn, caller:Method, callerCtx:Context, callee:Method, calleeCtx:Context) +.type HContext = Heap +.type Context = Heap - // new - VarPointsTo(heap, ctx, var, ctx) :- - Reachable(method, ctx, _), - AssignHeapAllocation(_, _, heap, var, method, _). - - // assign - VarPointsTo(heap, hctx, to, ctx) :- - Reachable(method, ctx, _), - VarPointsTo(heap, hctx, from, ctx), - AssignLocal(_, _, from, to, method). - - // cast - VarPointsTo(heap, hctx, to, ctx) :- - Reachable(method, ctx, _), - AssignCast(_, _, from, to, _, method), - VarPointsTo(heap, hctx, from, ctx). - - // load field - VarPointsTo(heap, hctx, to, ctx) :- - Reachable(method, ctx, _), - LoadInstanceField(_, _, to, base, field, method), - VarPointsTo(baseHeap, bhCtx, base, ctx), - InstanceFieldPointsTo(heap, hctx, baseHeap, bhCtx, field). - - // store field - InstanceFieldPointsTo(heap, hctx, baseHeap, bhCtx, field) :- - Reachable(method, ctx, _), - StoreInstanceField(_, _, from, base, field, method), - VarPointsTo(heap, hctx, from, ctx), - VarPointsTo(baseHeap, bhCtx, base, ctx). - - // load staic field - VarPointsTo(heap, hctx, to, ctx) :- - Reachable(method, ctx, _), - LoadStaticField(_, _, to, field, method), - StaticFieldPointsTo(heap, hctx, field). - - // store static field - StaticFieldPointsTo(heap, hctx, field) :- - Reachable(method, ctx, _), - StoreStaticField(_, _, from, field, method), - VarPointsTo(heap, hctx, from, ctx). - - // load from array - VarPointsTo(heap, hctx, to, ctx) :- - Reachable(method, ctx, _), - LoadArrayIndex(_, _, to, base, method), - VarPointsTo(baseHeap, bhCtx, base, ctx), - ArrayIndexPointsTo(heap, hctx, baseHeap, bhCtx). - - // store into array - ArrayIndexPointsTo(heap, hctx, baseHeap, bhCtx) :- - Reachable(method, ctx, _), - StoreArrayIndex(_, _, from, base, method), - VarPointsTo(heap, hctx, from, ctx), - VarPointsTo(baseHeap, bhCtx, base, ctx). - - Reachable(callee, calleeCtx, n+1), - CallGraph(insn, caller, callerCtx, callee, calleeCtx) :- - Reachable(caller, callerCtx, n), - n < MAXSTEP, - SpecialMethodInvocation(insn, _, callee, base, caller), - VarPointsTo(baseHeap, _, base, callerCtx), - calleeCtx = baseHeap. - - Reachable(callee, calleeCtx, n+1), - CallGraph(insn, caller, callerCtx, callee, calleeCtx) :- - Reachable(caller, callerCtx, n), - n < MAXSTEP, - StaticMethodInvocation(insn, _, callee, caller), - calleeCtx = insn. // maybe have bug - - Reachable(callee, calleeCtx, n+1), - CallGraph(insn, caller, callerCtx, callee, calleeCtx) :- - Reachable(caller, callerCtx, n), - n < MAXSTEP, - VirtualMethodInvocation(insn, _, method, base, caller), - VarPointsTo(baseHeap, _, base, callerCtx), - NormalHeap(baseHeap, class), - MethodInfo(method, simplename, _, _, _, descriptor, _), - Dispatch(simplename, descriptor, class, callee), - calleeCtx = baseHeap. - - // param - VarPointsTo(heap, hctx, param, calleeCtx) :- - CallGraph(insn, _, callerCtx, callee, calleeCtx), - ActualParam(n, insn, arg), - FormalParam(n, callee, param), - VarPointsTo(heap, hctx, arg, callerCtx), - NormalHeap(heap, _). - - // return - VarPointsTo(heap, hctx, return, callerCtx) :- - CallGraph(insn, _, callerCtx, callee, calleeCtx), - Return(_, _, var, callee), - AssignReturnValue(insn, return), - VarPointsTo(heap, hctx, var, calleeCtx). - - // this - VarPointsTo(heap, hctx, this, calleeCtx) :- - CallGraph(insn, _, callerCtx, callee, calleeCtx), +.comp OneObjectSensitivePT : AbstractContextSensitivePT{ + .override SelectInvocationContext + SelectInvocationContext(callerCtx, insn, baseHeap, hctx, calleeCtx) :- + Reachable(caller, callerCtx, _), ( - VirtualMethodInvocation(insn, _, _, base, _); - SpecialMethodInvocation(insn, _, _, base, _) + SpecialMethodInvocation(insn, _, _, base, caller); + VirtualMethodInvocation(insn, _, _, base, caller) ), - ThisVar(callee, this), - VarPointsTo(heap, hctx, base, callerCtx). + VarPointsTo(baseHeap, hctx, base, callerCtx), + calleeCtx = baseHeap. } \ No newline at end of file From 5f15b439a8dbb0f1e7711aaaaa38baf3308569a5 Mon Sep 17 00:00:00 2001 From: yxxx Date: Sun, 1 May 2022 22:39:58 +0800 Subject: [PATCH 19/36] refactor one type sensitive points-to --- example/one-type-sensitive-pt-example-1.dl | 4 +- logic/one-type-sensitive-pt.dl | 128 ++------------------- 2 files changed, 14 insertions(+), 118 deletions(-) diff --git a/example/one-type-sensitive-pt-example-1.dl b/example/one-type-sensitive-pt-example-1.dl index 741891e..f24d9b2 100644 --- a/example/one-type-sensitive-pt-example-1.dl +++ b/example/one-type-sensitive-pt-example-1.dl @@ -1,10 +1,10 @@ #define MAXSTEP 8 #include "../logic/one-type-sensitive-pt.dl" -.init typeDemo1 = OneTypeSensitivePT +.init typeDemo1 = OneTypeSensitivePT typeDemo1.Reachable("", "initCtx", 0). .output typeDemo1.VarPointsTo -.init typeDemo2 = OneTypeSensitivePT +.init typeDemo2 = OneTypeSensitivePT typeDemo2.Reachable("", "initCtx", 0). .output typeDemo2.VarPointsTo \ No newline at end of file diff --git a/logic/one-type-sensitive-pt.dl b/logic/one-type-sensitive-pt.dl index bdf9efc..fa0f716 100644 --- a/logic/one-type-sensitive-pt.dl +++ b/logic/one-type-sensitive-pt.dl @@ -1,123 +1,19 @@ #pragma once -#include "utils.dl" +#include "abstract-context-sensitive-pt.dl" -.comp OneTypeSensitivePT{ - .decl VarPointsTo(heap:Heap, hctx:Context, var:Var, vCtx:Context) - .decl InstanceFieldPointsTo(heap:Heap, hctx:Context, baseHeap:Heap, bhCtx:Context, field:Field) - .decl StaticFieldPointsTo(heap:Heap, hctx:Context, field:Field) - .decl ArrayIndexPointsTo(heap:Heap, hctx:Context, baseHeap:Heap, bhCtx:Context) - .decl Reachable(method:Method, ctx:Context, n:number) - .decl CallGraph(insn:Insn, caller:Method, callerCtx:Context, callee:Method, calleeCtx:Context) +.type HContext = Class +.type Context = Class - // new - VarPointsTo(heap, ctx, var, ctx) :- - Reachable(method, ctx, _), - AssignHeapAllocation(_, _, heap, var, method, _). - - // assign - VarPointsTo(heap, hctx, to, ctx) :- - Reachable(method, ctx, _), - VarPointsTo(heap, hctx, from, ctx), - AssignLocal(_, _, from, to, method). - - // cast - VarPointsTo(heap, hctx, to, ctx) :- - Reachable(method, ctx, _), - AssignCast(_, _, from, to, _, method), - VarPointsTo(heap, hctx, from, ctx). - - // load field - VarPointsTo(heap, hctx, to, ctx) :- - Reachable(method, ctx, _), - LoadInstanceField(_, _, to, base, field, method), - VarPointsTo(baseHeap, bhCtx, base, ctx), - InstanceFieldPointsTo(heap, hctx, baseHeap, bhCtx, field). - - // store field - InstanceFieldPointsTo(heap, hctx, baseHeap, bhCtx, field) :- - Reachable(method, ctx, _), - StoreInstanceField(_, _, from, base, field, method), - VarPointsTo(heap, hctx, from, ctx), - VarPointsTo(baseHeap, bhCtx, base, ctx). - - // load staic field - VarPointsTo(heap, hctx, to, ctx) :- - Reachable(method, ctx, _), - LoadStaticField(_, _, to, field, method), - StaticFieldPointsTo(heap, hctx, field). - - // store static field - StaticFieldPointsTo(heap, hctx, field) :- - Reachable(method, ctx, _), - StoreStaticField(_, _, from, field, method), - VarPointsTo(heap, hctx, from, ctx). - - // load from array - VarPointsTo(heap, hctx, to, ctx) :- - Reachable(method, ctx, _), - LoadArrayIndex(_, _, to, base, method), - VarPointsTo(baseHeap, bhCtx, base, ctx), - ArrayIndexPointsTo(heap, hctx, baseHeap, bhCtx). - - // store into array - ArrayIndexPointsTo(heap, hctx, baseHeap, bhCtx) :- - Reachable(method, ctx, _), - StoreArrayIndex(_, _, from, base, method), - VarPointsTo(heap, hctx, from, ctx), - VarPointsTo(baseHeap, bhCtx, base, ctx). - - Reachable(callee, calleeCtx, n+1), - CallGraph(insn, caller, callerCtx, callee, calleeCtx) :- - Reachable(caller, callerCtx, n), - n < MAXSTEP, - SpecialMethodInvocation(insn, _, callee, base, caller), - VarPointsTo(baseHeap, _, base, callerCtx), - AssignHeapAllocation(_, _, baseHeap, _, inmethod, _), - MethodInfo(inmethod, _, _, inType, _, _, _), - calleeCtx = inType. - - Reachable(callee, calleeCtx, n+1), - CallGraph(insn, caller, callerCtx, callee, calleeCtx) :- - Reachable(caller, callerCtx, n), - n < MAXSTEP, - StaticMethodInvocation(insn, _, callee, caller), - calleeCtx = insn. // maybe have bug - - Reachable(callee, calleeCtx, n+1), - CallGraph(insn, caller, callerCtx, callee, calleeCtx) :- - Reachable(caller, callerCtx, n), - n < MAXSTEP, - VirtualMethodInvocation(insn, _, method, base, caller), - VarPointsTo(baseHeap, _, base, callerCtx), - NormalHeap(baseHeap, class), - MethodInfo(method, simplename, _, _, _, descriptor, _), - Dispatch(simplename, descriptor, class, callee), +.comp OneTypeSensitivePT : AbstractContextSensitivePT{ + .override SelectInvocationContext + SelectInvocationContext(callerCtx, insn, baseHeap, hctx, calleeCtx) :- + Reachable(caller, callerCtx, _), + ( + SpecialMethodInvocation(insn, _, _, base, caller); + VirtualMethodInvocation(insn, _, _, base, caller) + ), + VarPointsTo(baseHeap, hctx, base, callerCtx), AssignHeapAllocation(_, _, baseHeap, _, inmethod, _), MethodInfo(inmethod, _, _, inType, _, _, _), calleeCtx = inType. - - // param - VarPointsTo(heap, hctx, param, calleeCtx) :- - CallGraph(insn, _, callerCtx, callee, calleeCtx), - ActualParam(n, insn, arg), - FormalParam(n, callee, param), - VarPointsTo(heap, hctx, arg, callerCtx), - NormalHeap(heap, _). - - // return - VarPointsTo(heap, hctx, return, callerCtx) :- - CallGraph(insn, _, callerCtx, callee, calleeCtx), - Return(_, _, var, callee), - AssignReturnValue(insn, return), - VarPointsTo(heap, hctx, var, calleeCtx). - - // this - VarPointsTo(heap, hctx, this, calleeCtx) :- - CallGraph(insn, _, callerCtx, callee, calleeCtx), - ( - VirtualMethodInvocation(insn, _, _, base, _); - SpecialMethodInvocation(insn, _, _, base, _) - ), - ThisVar(callee, this), - VarPointsTo(heap, hctx, base, callerCtx). } \ No newline at end of file From f3f223404c5330553c9798fd9bbeb9a325ba826c Mon Sep 17 00:00:00 2001 From: yxxx Date: Sun, 1 May 2022 23:03:26 +0800 Subject: [PATCH 20/36] refactor ptaint use compotent inheritance --- example/ptaint-example-1.dl | 6 +++--- example/ptaint-example-2.dl | 12 ++++++------ logic/ptaint.dl | 23 +++++++++++------------ 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/example/ptaint-example-1.dl b/example/ptaint-example-1.dl index 369f48b..8252655 100644 --- a/example/ptaint-example-1.dl +++ b/example/ptaint-example-1.dl @@ -2,7 +2,7 @@ .init ptaint = PTaint -ptaint.cipt.Reachable(""). +ptaint.Reachable(""). ptaint.SourceMethod(""). ptaint.SinkMethod("", 0). @@ -18,11 +18,11 @@ ptaint.SanitizeMethod(""). -ptaint.cipt.Reachable(method) :- +ptaint.Reachable(method) :- EntryMethod(method). NormalHeap(heap, class), -ptaint.cipt.VarPointsTo(heap, this) :- +ptaint.VarPointsTo(heap, this) :- ThisVar(method, this), EntryMethod(method), VarType(this, class), @@ -19,8 +19,8 @@ ptaint.cipt.VarPointsTo(heap, this) :- NormalHeap(heap, class), ptaint.TaintHeap(insn, taintHeap), -ptaint.cipt.VarPointsTo(heap, param), -ptaint.cipt.VarPointsTo(taintHeap, param) :- +ptaint.VarPointsTo(heap, param), +ptaint.VarPointsTo(taintHeap, param) :- EntryMethod(method), FormalParam(_, method, param), VarType(param, class), @@ -43,11 +43,11 @@ ptaint.SanitizeMethod(" Date: Fri, 6 May 2022 10:56:01 +0800 Subject: [PATCH 21/36] remove type parameterisation --- example/one-callsite-sensitive-pt-example-1.dl | 4 ++-- example/one-object-sensitive-pt-example-1.dl | 4 ++-- example/one-type-sensitive-pt-example-1.dl | 4 ++-- logic/one-callsite-sensitive-pt.dl | 2 +- logic/one-object-sensitive-pt.dl | 2 +- logic/one-type-sensitive-pt.dl | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/example/one-callsite-sensitive-pt-example-1.dl b/example/one-callsite-sensitive-pt-example-1.dl index 0a07ead..d640850 100644 --- a/example/one-callsite-sensitive-pt-example-1.dl +++ b/example/one-callsite-sensitive-pt-example-1.dl @@ -1,10 +1,10 @@ #define MAXSTEP 8 #include "../logic/one-callsite-sensitive-pt.dl" -.init callsitecsDemo1 = OneCallsiteSensitivePT +.init callsitecsDemo1 = OneCallsiteSensitivePT callsitecsDemo1.Reachable("", "initCtx", 0). .output callsitecsDemo1.VarPointsTo -.init callsitecsDemo2 = OneCallsiteSensitivePT +.init callsitecsDemo2 = OneCallsiteSensitivePT callsitecsDemo2.Reachable("", "initCtx", 0). .output callsitecsDemo2.VarPointsTo \ No newline at end of file diff --git a/example/one-object-sensitive-pt-example-1.dl b/example/one-object-sensitive-pt-example-1.dl index becfa38..69f37d1 100644 --- a/example/one-object-sensitive-pt-example-1.dl +++ b/example/one-object-sensitive-pt-example-1.dl @@ -1,10 +1,10 @@ #define MAXSTEP 8 #include "../logic/one-object-sensitive-pt.dl" -.init objectDemo1 = OneObjectSensitivePT +.init objectDemo1 = OneObjectSensitivePT objectDemo1.Reachable("", "initCtx", 0). .output objectDemo1.VarPointsTo -.init objectDemo2 = OneObjectSensitivePT +.init objectDemo2 = OneObjectSensitivePT objectDemo2.Reachable("", "initCtx", 0). .output objectDemo2.VarPointsTo \ No newline at end of file diff --git a/example/one-type-sensitive-pt-example-1.dl b/example/one-type-sensitive-pt-example-1.dl index f24d9b2..741891e 100644 --- a/example/one-type-sensitive-pt-example-1.dl +++ b/example/one-type-sensitive-pt-example-1.dl @@ -1,10 +1,10 @@ #define MAXSTEP 8 #include "../logic/one-type-sensitive-pt.dl" -.init typeDemo1 = OneTypeSensitivePT +.init typeDemo1 = OneTypeSensitivePT typeDemo1.Reachable("", "initCtx", 0). .output typeDemo1.VarPointsTo -.init typeDemo2 = OneTypeSensitivePT +.init typeDemo2 = OneTypeSensitivePT typeDemo2.Reachable("", "initCtx", 0). .output typeDemo2.VarPointsTo \ No newline at end of file diff --git a/logic/one-callsite-sensitive-pt.dl b/logic/one-callsite-sensitive-pt.dl index 253c6c2..1267533 100644 --- a/logic/one-callsite-sensitive-pt.dl +++ b/logic/one-callsite-sensitive-pt.dl @@ -4,7 +4,7 @@ .type HContext = Insn .type Context = Insn -.comp OneCallsiteSensitivePT : AbstractContextSensitivePT{ +.comp OneCallsiteSensitivePT: AbstractContextSensitivePT{ .override SelectInvocationContext SelectInvocationContext(callerCtx, insn, baseHeap, hctx, calleeCtx) :- Reachable(caller, callerCtx, _), diff --git a/logic/one-object-sensitive-pt.dl b/logic/one-object-sensitive-pt.dl index 7ca1f24..d3f204d 100644 --- a/logic/one-object-sensitive-pt.dl +++ b/logic/one-object-sensitive-pt.dl @@ -4,7 +4,7 @@ .type HContext = Heap .type Context = Heap -.comp OneObjectSensitivePT : AbstractContextSensitivePT{ +.comp OneObjectSensitivePT: AbstractContextSensitivePT{ .override SelectInvocationContext SelectInvocationContext(callerCtx, insn, baseHeap, hctx, calleeCtx) :- Reachable(caller, callerCtx, _), diff --git a/logic/one-type-sensitive-pt.dl b/logic/one-type-sensitive-pt.dl index fa0f716..48ff9cf 100644 --- a/logic/one-type-sensitive-pt.dl +++ b/logic/one-type-sensitive-pt.dl @@ -4,7 +4,7 @@ .type HContext = Class .type Context = Class -.comp OneTypeSensitivePT : AbstractContextSensitivePT{ +.comp OneTypeSensitivePT: AbstractContextSensitivePT{ .override SelectInvocationContext SelectInvocationContext(callerCtx, insn, baseHeap, hctx, calleeCtx) :- Reachable(caller, callerCtx, _), From 62082d3e76a9f428dd63ed3dc7a20949eff74402 Mon Sep 17 00:00:00 2001 From: yxxx Date: Fri, 6 May 2022 15:22:39 +0800 Subject: [PATCH 22/36] add abstract-context-sensitive-pt --- logic/abstract-context-sensitive-pt.dl | 127 +++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 logic/abstract-context-sensitive-pt.dl diff --git a/logic/abstract-context-sensitive-pt.dl b/logic/abstract-context-sensitive-pt.dl new file mode 100644 index 0000000..1967fab --- /dev/null +++ b/logic/abstract-context-sensitive-pt.dl @@ -0,0 +1,127 @@ +#pragma once +#include "utils.dl" + +.comp AbstractContextSensitivePT{ + .decl VarPointsTo(heap:Heap, hctx:HContext, var:Var, vCtx:Context) + .decl InstanceFieldPointsTo(heap:Heap, hctx:Context, baseHeap:Heap, bhCtx:Context, field:Field) + .decl StaticFieldPointsTo(heap:Heap, hctx:Context, field:Field) + .decl ArrayIndexPointsTo(heap:Heap, hctx:Context, baseHeap:Heap, bhCtx:Context) + .decl Reachable(method:Method, ctx:Context, n:number) + .decl CallGraph(insn:Insn, caller:Method, callerCtx:Context, callee:Method, calleeCtx:Context) + + .decl SelectInvocationContext(callerCtx:Context, invocation:Insn, baseHeap:Heap, hctx:HContext, calleeCtx:Context) overridable + .decl SelectStaticInvocationContext(callerCtx:Context, invocation:Insn, calleeCtx:Context) overridable + + SelectStaticInvocationContext(callerCtx, insn, calleeCtx) :- + Reachable(caller, callerCtx, _), + StaticMethodInvocation(insn, _, _, caller), + calleeCtx = callerCtx. + + // new + VarPointsTo(heap, ctx, var, ctx) :- + Reachable(method, ctx, _), + AssignHeapAllocation(_, _, heap, var, method, _). + + // assign + VarPointsTo(heap, hctx, to, ctx) :- + Reachable(method, ctx, _), + VarPointsTo(heap, hctx, from, ctx), + AssignLocal(_, _, from, to, method). + + // cast + VarPointsTo(heap, hctx, to, ctx) :- + Reachable(method, ctx, _), + AssignCast(_, _, from, to, _, method), + VarPointsTo(heap, hctx, from, ctx). + + // load field + VarPointsTo(heap, hctx, to, ctx) :- + Reachable(method, ctx, _), + LoadInstanceField(_, _, to, base, field, method), + VarPointsTo(baseHeap, bhCtx, base, ctx), + InstanceFieldPointsTo(heap, hctx, baseHeap, bhCtx, field). + + // store field + InstanceFieldPointsTo(heap, hctx, baseHeap, bhCtx, field) :- + Reachable(method, ctx, _), + StoreInstanceField(_, _, from, base, field, method), + VarPointsTo(heap, hctx, from, ctx), + VarPointsTo(baseHeap, bhCtx, base, ctx). + + // load staic field + VarPointsTo(heap, hctx, to, ctx) :- + Reachable(method, ctx, _), + LoadStaticField(_, _, to, field, method), + StaticFieldPointsTo(heap, hctx, field). + + // store static field + StaticFieldPointsTo(heap, hctx, field) :- + Reachable(method, ctx, _), + StoreStaticField(_, _, from, field, method), + VarPointsTo(heap, hctx, from, ctx). + + // load from array + VarPointsTo(heap, hctx, to, ctx) :- + Reachable(method, ctx, _), + LoadArrayIndex(_, _, to, base, method), + VarPointsTo(baseHeap, bhCtx, base, ctx), + ArrayIndexPointsTo(heap, hctx, baseHeap, bhCtx). + + // store into array + ArrayIndexPointsTo(heap, hctx, baseHeap, bhCtx) :- + Reachable(method, ctx, _), + StoreArrayIndex(_, _, from, base, method), + VarPointsTo(heap, hctx, from, ctx), + VarPointsTo(baseHeap, bhCtx, base, ctx). + + Reachable(callee, calleeCtx, n+1), + CallGraph(insn, caller, callerCtx, callee, calleeCtx) :- + Reachable(caller, callerCtx, n), + n < MAXSTEP, + SpecialMethodInvocation(insn, _, callee, base, caller), + VarPointsTo(baseHeap, hctx, base, calleeCtx), + SelectInvocationContext(callerCtx, insn, baseHeap, hctx, callerCtx). + + Reachable(callee, calleeCtx, n+1), + CallGraph(insn, caller, callerCtx, callee, calleeCtx) :- + Reachable(caller, callerCtx, n), + n < MAXSTEP, + StaticMethodInvocation(insn, _, callee, caller), + SelectStaticInvocationContext(callerCtx, insn, calleeCtx). + + Reachable(callee, calleeCtx, n+1), + CallGraph(insn, caller, callerCtx, callee, calleeCtx) :- + Reachable(caller, callerCtx, n), + n < MAXSTEP, + VirtualMethodInvocation(insn, _, method, base, caller), + VarPointsTo(baseHeap, hctx, base, callerCtx), + NormalHeap(baseHeap, class), + MethodInfo(method, simplename, _, _, _, descriptor, _), + Dispatch(simplename, descriptor, class, callee), + SelectInvocationContext(callerCtx, insn, baseHeap, hctx, calleeCtx). + + // param + VarPointsTo(heap, hctx, param, calleeCtx) :- + CallGraph(insn, _, callerCtx, callee, calleeCtx), + ActualParam(n, insn, arg), + FormalParam(n, callee, param), + VarPointsTo(heap, hctx, arg, callerCtx), + NormalHeap(heap, _). + + // return + VarPointsTo(heap, hctx, return, callerCtx) :- + CallGraph(insn, _, callerCtx, callee, calleeCtx), + Return(_, _, var, callee), + AssignReturnValue(insn, return), + VarPointsTo(heap, hctx, var, calleeCtx). + + // this + VarPointsTo(heap, hctx, this, calleeCtx) :- + CallGraph(insn, _, callerCtx, callee, calleeCtx), + ( + VirtualMethodInvocation(insn, _, _, base, _); + SpecialMethodInvocation(insn, _, _, base, _) + ), + ThisVar(callee, this), + VarPointsTo(heap, hctx, base, callerCtx). +} \ No newline at end of file From 1d644ea6edbd34d955d5fb64f03bbae2ef7878e7 Mon Sep 17 00:00:00 2001 From: yxxx Date: Fri, 6 May 2022 15:25:52 +0800 Subject: [PATCH 23/36] add context sensitive ptaint --- example/cs-ptaint-example-1.dl | 39 +++++++++++++++++++++ logic/cs-ptaint.dl | 64 ++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 example/cs-ptaint-example-1.dl create mode 100644 logic/cs-ptaint.dl diff --git a/example/cs-ptaint-example-1.dl b/example/cs-ptaint-example-1.dl new file mode 100644 index 0000000..d1614c0 --- /dev/null +++ b/example/cs-ptaint-example-1.dl @@ -0,0 +1,39 @@ +#define MAXSTEP 8 +//#include "../logic/one-callsite-sensitive-pt.dl" +#include "../logic/cs-ptaint.dl" +#include "../logic/one-object-sensitive-pt.dl" + +//.comp MyCallsitePtaint : CSPTaint, OneCallsiteSensitivePT{ +//} +//.init csptaint = MyCallsitePtaint +.comp MyObjectPtaint : CSPTaint, OneObjectSensitivePT{ +} + + +.init csptaint = MyObjectPtaint + + +csptaint.Reachable("", "initCtx", 0). + +csptaint.SourceMethod(""). +csptaint.SinkMethod("", 0). + +csptaint.BaseToRetTransfer(""). +csptaint.BaseToRetTransfer(""). + +csptaint.ArgToRetTransfer("", 0). + +csptaint.SanitizeMethod(""). + + +.decl TaintVar(var:Var, vCtx:Context) + +TaintVar(var, vCtx) :- + csptaint.VarPointsTo(heap, _, var, vCtx), + csptaint.TaintHeap(_, heap). + +.output TaintVar + +.output csptaint.TaintHeap +.output csptaint.TransferTaint +.output csptaint.VarPointsTo \ No newline at end of file diff --git a/logic/cs-ptaint.dl b/logic/cs-ptaint.dl new file mode 100644 index 0000000..524394c --- /dev/null +++ b/logic/cs-ptaint.dl @@ -0,0 +1,64 @@ +#pragma once +#include "utils.dl" +#include "abstract-context-sensitive-pt.dl" + +.comp CSPTaint{ + //.init cspt = AbstractContextSensitivePT + //.init cspt = CSPT + + .decl TaintHeap(insn:Insn, heap:Heap) + .decl SourceMethod(method:Method) + .decl SinkMethod(method:Method, n:number) + + .decl SanitizeMethod(method:Method) + + .decl BaseToRetTransfer(method:Method) + .decl ArgToRetTransfer(method:Method, n:number) + .decl IsTaintedFrom(insn:Insn, from:Var, fromCtx:Context, to:Var, toCtx:Context) + .decl TransferTaint(heap:Heap, newHeap:Heap) + + + // taint arg to param + VarPointsTo(heap, hctx, param, calleeCtx) :- + CallGraph(insn, _, callerCtx, callee, calleeCtx), + ActualParam(n, insn, arg), + FormalParam(n, callee, param), + VarPointsTo(heap, hctx, arg, callerCtx), + TaintHeap(_, heap), // sensitive? + !SanitizeMethod(callee). + + + TaintHeap(insn, heap), + VarPointsTo(heap, hctx, to, callerCtx):- + SourceMethod(callee), + CallGraph(insn, _, callerCtx, callee, _), + AssignReturnValue(insn, to), + heap = cat("NewTainted::", insn), + hctx = "Mock::". + + IsTaintedFrom(insn, base, callerCtx, ret, callerCtx) :- + CallGraph(insn, _, callerCtx, callee, _), + BaseToRetTransfer(callee), + ( + VirtualMethodInvocation(insn, _, _, base, _); + SpecialMethodInvocation(insn, _, _, base, _) + ), + AssignReturnValue(insn, ret). + + IsTaintedFrom(insn, arg, callerCtx, ret, callerCtx) :- + CallGraph(insn, _, callerCtx, callee, _), + ArgToRetTransfer(callee, n), + ActualParam(n, insn, arg), + AssignReturnValue(insn, ret). + + TaintHeap(insn, newHeap), + TransferTaint(heap, newHeap), + VarPointsTo(heap, hctx, var, vCtx) :- + IsTaintedFrom(insn, from, fromCtx, to, toCtx), + VarPointsTo(heap, hctx, from, fromCtx), + TaintHeap(_, heap), + newHeap = cat("TransferTaint::", insn), + VarPointsTo(oldHeap, oldHCtx, to, toCtx), + VarPointsTo(oldHeap, oldHCtx, var, vCtx), + AssignHeapAllocation(_, _, oldHeap, var, _, _). +} \ No newline at end of file From 1d1c189f80ad39e4951eb435d9c6c94fdb59fa7d Mon Sep 17 00:00:00 2001 From: yxxx Date: Fri, 6 May 2022 15:26:22 +0800 Subject: [PATCH 24/36] update ptaint --- logic/ptaint.dl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/logic/ptaint.dl b/logic/ptaint.dl index a557cd6..2d83ea0 100644 --- a/logic/ptaint.dl +++ b/logic/ptaint.dl @@ -35,7 +35,10 @@ IsTaintedFrom(insn, base, ret) :- CallGraph(insn, _, callee), BaseToRetTransfer(callee), - VirtualMethodInvocation(insn, _, _, base, _), + ( + VirtualMethodInvocation(insn, _, _, base, _); + SpecialMethodInvocation(insn, _, _, base, _) + ), AssignReturnValue(insn, ret). IsTaintedFrom(insn, arg, ret) :- From b99ea8652f2f20053e010f9d92b71f7b515e2586 Mon Sep 17 00:00:00 2001 From: yxxx Date: Fri, 6 May 2022 15:33:25 +0800 Subject: [PATCH 25/36] update readme --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e043fa5..f81858c 100644 --- a/README.md +++ b/README.md @@ -37,9 +37,12 @@ you can use the docker we builded like docker-compose.yml - [x] 一阶上下文对象敏感指针分析 - [x] 一阶上下文类型敏感指针分析 - [ ] 可选择上下文敏感指针分析 -- [ ] 污点分析 +- [x] 污点分析 - [x] 上下文无关ptaint - - [ ] 上下文敏感ptaint + - [x] 上下文敏感ptaint +- [ ] 输出sarif +- [ ] 实现JackEE +- [ ] 性能优化 ## Usage From 6edd373b1086affe9914f78caa8f1da9bc44864a Mon Sep 17 00:00:00 2001 From: yxxx Date: Mon, 30 May 2022 15:43:46 +0800 Subject: [PATCH 26/36] add support info --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index f81858c..c3a2af1 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,16 @@ you can use the docker we builded like docker-compose.yml 见docs文件夹 +## Support + +在使用中遇到什么问题,可以通过 + +- email: help@bytecodedl.com +- github: issue/discussion +- telegram: [bytecodedl](https://t.me/bytecodedl) + +三种途径向我们反馈 + ## Plugin - IDEA From 1328e80a551ca3e73bdca1343550cf9511905e2e Mon Sep 17 00:00:00 2001 From: yxxx Date: Sun, 28 Aug 2022 22:32:50 +0800 Subject: [PATCH 27/36] fix little bug --- logic/cha.dl | 2 +- logic/utils.dl | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/logic/cha.dl b/logic/cha.dl index 77387fb..dcb7e91 100644 --- a/logic/cha.dl +++ b/logic/cha.dl @@ -14,7 +14,7 @@ SinkMethod(method) :- SinkDesc(simplename, class), SubEqClass(subeqclass, class), - !ClassModifier("abstract", subeqclass), + !MethodModifier("abstract", method), MethodInfo(method, simplename, _, subeqclass, _, _, _). EntryMethod(method), diff --git a/logic/utils.dl b/logic/utils.dl index 9bcc016..a7e564a 100644 --- a/logic/utils.dl +++ b/logic/utils.dl @@ -7,6 +7,9 @@ .decl SubClass(subclass:Class, class:Class) .decl SubEqClass(subeqclass:Class, class:Class) +SubEqClass("byte", "byte"). +SubEqClass("byte[]", "byte[]"). + SubEqClass(subclass, class) :- SubClass(subclass, class). SubEqClass(class, class) :- ClassType(class). From 244b7ed954d0d50353565d3fdd99094116af894d Mon Sep 17 00:00:00 2001 From: yxxx Date: Fri, 10 Mar 2023 17:58:44 +0800 Subject: [PATCH 28/36] update import headers --- neo4j/CallEdgeHeader.csv | 2 +- neo4j/CallNodeHeader.csv | 2 +- neo4j/ChaEdgeHeader.csv | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 neo4j/ChaEdgeHeader.csv diff --git a/neo4j/CallEdgeHeader.csv b/neo4j/CallEdgeHeader.csv index a163d36..eec8984 100644 --- a/neo4j/CallEdgeHeader.csv +++ b/neo4j/CallEdgeHeader.csv @@ -1 +1 @@ -:START_ID :END_ID \ No newline at end of file +:START_ID(Method) :END_ID(Method) \ No newline at end of file diff --git a/neo4j/CallNodeHeader.csv b/neo4j/CallNodeHeader.csv index bff2ea0..27f9b61 100644 --- a/neo4j/CallNodeHeader.csv +++ b/neo4j/CallNodeHeader.csv @@ -1 +1 @@ -method:ID :LABEL \ No newline at end of file +method:ID(Method) :LABEL \ No newline at end of file diff --git a/neo4j/ChaEdgeHeader.csv b/neo4j/ChaEdgeHeader.csv new file mode 100644 index 0000000..27b0681 --- /dev/null +++ b/neo4j/ChaEdgeHeader.csv @@ -0,0 +1 @@ +:START_ID(Method) method :END_ID(Method) \ No newline at end of file From 774df7eb2d2d7a8d4a798ee2a198576cdb2fe493 Mon Sep 17 00:00:00 2001 From: yxxx Date: Mon, 13 Mar 2023 22:45:52 +0800 Subject: [PATCH 29/36] add simple-cha and log4shell example --- example/simple-cha-log4shell.dl | 19 ++++++ logic/simple-cha.dl | 105 ++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 example/simple-cha-log4shell.dl create mode 100644 logic/simple-cha.dl diff --git a/example/simple-cha-log4shell.dl b/example/simple-cha-log4shell.dl new file mode 100644 index 0000000..e4ac3ff --- /dev/null +++ b/example/simple-cha-log4shell.dl @@ -0,0 +1,19 @@ +#define MAXSTEP 33 + +#include "../logic/simple-cha.dl" + + +SinkDesc("lookup", "javax.naming.Context"). + +// init entrypoint +EntryPoint(simplename, descriptor, class) :- + MethodInfo(_, simplename, _, class, _, descriptor, _), + simplename = "error", + class = "org.apache.logging.log4j.spi.AbstractLogger", + descriptor = "(Ljava/lang/String;)V". + + +.output EntryPoint +.output EntryMethod +.output SinkMethod +.output SimpleCallGraph \ No newline at end of file diff --git a/logic/simple-cha.dl b/logic/simple-cha.dl new file mode 100644 index 0000000..240a3b2 --- /dev/null +++ b/logic/simple-cha.dl @@ -0,0 +1,105 @@ +#pragma once +#include "utils.dl" + +.decl EntryPoint(simplename:symbol, descriptor:symbol, class:Class) +.decl Reachable(method:Method, step:number) +.decl SinkDesc(simplename:symbol, class:Class) +.decl SinkMethod(method:Method) +.decl EntryMethod(method:Method) +.decl BanCaller(method:Method) + +BanCaller(method) :- + MethodInfo(method, simplename, _, class, _, _, _), + contains("java.util", class). + +.output BanCaller + + +.decl CallGraph(insn:Insn, caller:Method, callee:Method) +.decl SimpleCallGraph(insn:Insn, caller:Method, callee:Method) +.decl ChaGraph(caller:Method, implementation:Method, callee:Method) + +SinkMethod(method) :- + SinkDesc(simplename, class), + SubEqClass(subeqclass, class), + !MethodModifier("abstract", method), + MethodInfo(method, simplename, _, subeqclass, _, _, _). + +EntryMethod(method), +Reachable(method, 0) :- + EntryPoint(simplename, descriptor, class), + Dispatch(simplename, descriptor, class, method). + +Reachable(callee, n+1), +SimpleCallGraph(insn, caller, callee), +CallGraph(insn, caller, callee) :- + Reachable(caller, n), + !BanCaller(caller), + n < MAXSTEP, + SpecialMethodInvocation(insn, _, callee, _, caller). + +Reachable(callee, n+1), +SimpleCallGraph(insn, caller, callee), +CallGraph(insn, caller, callee) :- + Reachable(caller, n), + !BanCaller(caller), + n < MAXSTEP, + StaticMethodInvocation(insn, _, callee, caller). + +Reachable(method, n+1), +Reachable(callee, n+1), +SimpleCallGraph(insn, caller, method), +CallGraph(insn, caller, callee) :- + Reachable(caller, n), + !BanCaller(caller), + n < MAXSTEP, + VirtualMethodInvocation(insn, _, method, receiver, caller), + MethodInfo(method, simplename, _, _, _, descriptor, _), + VarType(receiver, class), + SubEqClass(subeqclass, class), + !ClassModifier("abstract", subeqclass), + Dispatch(simplename, descriptor, subeqclass, callee). + +Reachable(callee, n+1), +SimpleCallGraph(insn, caller, callee), +CallGraph(insn, caller, callee) :- + Reachable(caller, n), + !BanCaller(caller), + n < MAXSTEP, + StaticMethodInvocation(insn, _, method, caller), + MethodInfo(method, "doPrivileged", _, "java.security.AccessController", _, _, _), + ActualParam(0, insn, param), + VarType(param, class), + MethodInfo(callee, "run", _, class, _, _, 0). + +ChaGraph(caller, implementation, callee) :- + Reachable(caller, _), + MethodInfo(caller, simplename, _, class, _, descriptor, _), + SubClass(subclass, class), + Dispatch(simplename, descriptor, subclass, implementation), + caller != implementation, + SimpleCallGraph(_, implementation, callee). + +.output ChaGraph + +.decl CallNode(node:Method, label:symbol) +.output CallNode + +CallNode(node, "method") :- + !EntryMethod(node), + !SinkMethod(node), + Reachable(node, _). + +CallNode(node, "sink") :- + Reachable(node, _), + SinkMethod(node). + +CallNode(node, "entry") :- + Reachable(node, _), + EntryMethod(node). + +.decl CallEdge(caller:Method, callee:Method) +.output CallEdge + +CallEdge(caller, callee) :- + SimpleCallGraph(_, caller, callee). From 3afb958e7d3c0a3262bd93cda304e1a4cc7c9365 Mon Sep 17 00:00:00 2001 From: yxxx Date: Mon, 13 Mar 2023 22:49:46 +0800 Subject: [PATCH 30/36] add import simple cha results script --- neoImportChaCall.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 neoImportChaCall.sh diff --git a/neoImportChaCall.sh b/neoImportChaCall.sh new file mode 100644 index 0000000..d1445e9 --- /dev/null +++ b/neoImportChaCall.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +dbname=$1$(date "+%m%d%H%M") + +neo4j-admin import --nodes=Method="/bytecodedl/neo4j/CallNodeHeader.csv,/bytecodedl/output/.*CallNode.csv" --relationships=Call="/bytecodedl/neo4j/CallEdgeHeader.csv,/bytecodedl/output/CallEdge.csv" --relationships=Cha="/bytecodedl/neo4j/ChaEdgeHeader.csv,/bytecodedl/output/ChaGraph.csv" --database=$dbname --delimiter="\t" + +if grep -q "dbms.active_database" /var/lib/neo4j/conf/neo4j.conf; then + sed -i -E "s/dbms.active_database=\w+/dbms.active_database=$dbname/g" /var/lib/neo4j/conf/neo4j.conf +else + echo "dbms.active_database=$dbname" >> /var/lib/neo4j/conf/neo4j.conf +fi \ No newline at end of file From 1e7ea622e5e06ca22b0354ab0e6839cedd6e6269 Mon Sep 17 00:00:00 2001 From: yxxx Date: Mon, 13 Mar 2023 23:02:36 +0800 Subject: [PATCH 31/36] neo4j docker add apoc plugin --- docker-compose.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 0f77ab8..364657e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,5 +14,10 @@ services: - "0.0.0.0:7687:7687" environment: - NEO4J_AUTH=neo4j/bytecodedl + - NEO4J_apoc_export_file_enabled=true + - NEO4J_apoc_import_file_enabled=true + - NEO4J_apoc_import_file_use__neo4j__config=true + - NEO4JLABS_PLUGINS=["apoc"] + - NEO4J_dbms_security_procedures_unrestricted=apoc.* volumes: - ./:/bytecodedl \ No newline at end of file From dc472472afc72d3112e8b614721f9f2f766c898a Mon Sep 17 00:00:00 2001 From: yxxx Date: Mon, 13 Mar 2023 23:03:57 +0800 Subject: [PATCH 32/36] add simple-cha --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c3a2af1..785250a 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ you can use the docker we builded like docker-compose.yml - [x] 搜索功能 - [x] 调用图分析 - [x] CHA + - [x] SIMPLE-CHA - [x] RTA - [ ] 指针分析 - [x] 上下文无关指针分析 From eb6449206de4a488c06df4f58edb5a7d763717de Mon Sep 17 00:00:00 2001 From: yxxx Date: Thu, 28 Dec 2023 19:13:36 +0800 Subject: [PATCH 33/36] add cha-log4shell example --- docker-compose.yml | 11 ++--------- example/cha-log4shell.dl | 18 ++++++++++++++++++ logic/cha.dl | 6 +++--- neo4j/CallEdgeHeader.csv | 2 +- neoImportCall-4.4.sh | 11 +++++++++++ neoImportCall.sh | 10 +++++----- neoImportChaCall-4.4.sh | 11 +++++++++++ neoImportChaCall.sh | 10 +++++----- 8 files changed, 56 insertions(+), 23 deletions(-) create mode 100644 example/cha-log4shell.dl create mode 100644 neoImportCall-4.4.sh create mode 100644 neoImportChaCall-4.4.sh diff --git a/docker-compose.yml b/docker-compose.yml index 364657e..e21273c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,17 +7,10 @@ services: volumes: - ./:/bytecodedl neo: - image: neo4j:4.4.4-community + image: neo4j-server:5.12.0-bytecodedl-pathfinder-1.0.0 restart: always ports: - "0.0.0.0:7474:7474" - "0.0.0.0:7687:7687" - environment: - - NEO4J_AUTH=neo4j/bytecodedl - - NEO4J_apoc_export_file_enabled=true - - NEO4J_apoc_import_file_enabled=true - - NEO4J_apoc_import_file_use__neo4j__config=true - - NEO4JLABS_PLUGINS=["apoc"] - - NEO4J_dbms_security_procedures_unrestricted=apoc.* volumes: - - ./:/bytecodedl \ No newline at end of file + - ./:/bytecodedl diff --git a/example/cha-log4shell.dl b/example/cha-log4shell.dl new file mode 100644 index 0000000..4b9f606 --- /dev/null +++ b/example/cha-log4shell.dl @@ -0,0 +1,18 @@ +#define MAXSTEP 33 +#define CHAO 1 + +#include "../logic/cha.dl" + +BanCaller(method) :- + MethodInfo(method, _, _, class, _, _, _), + !contains("org.apache.logging.log4j", class). + + +SinkDesc("lookup", "javax.naming.Context"). + +// init entrypoint +EntryPoint(simplename, descriptor, class) :- + MethodInfo(_, simplename, _, class, _, descriptor, _), + simplename = "error", + class = "org.apache.logging.log4j.spi.AbstractLogger", + descriptor = "(Ljava/lang/String;)V". \ No newline at end of file diff --git a/logic/cha.dl b/logic/cha.dl index dcb7e91..d290216 100644 --- a/logic/cha.dl +++ b/logic/cha.dl @@ -123,10 +123,10 @@ CallNode(node, "entry") :- RefinedReachable(node), EntryMethod(node). -.decl CallEdge(caller:Method, callee:Method) +.decl CallEdge(caller:Method, insn:Insn, callee:Method) .output CallEdge -CallEdge(caller, callee) :- +CallEdge(caller, insn, callee) :- RefinedReachable(caller), RefinedReachable(callee), - CallGraph(_, caller, callee). + CallGraph(insn, caller, callee). diff --git a/neo4j/CallEdgeHeader.csv b/neo4j/CallEdgeHeader.csv index eec8984..5e2a8da 100644 --- a/neo4j/CallEdgeHeader.csv +++ b/neo4j/CallEdgeHeader.csv @@ -1 +1 @@ -:START_ID(Method) :END_ID(Method) \ No newline at end of file +:START_ID(Method) insn :END_ID(Method) \ No newline at end of file diff --git a/neoImportCall-4.4.sh b/neoImportCall-4.4.sh new file mode 100644 index 0000000..0e82b0d --- /dev/null +++ b/neoImportCall-4.4.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +dbname=$1$(date "+%m%d%H%M") + +neo4j-admin database import full --relationships=Call="/bytecodedl/neo4j/CallEdgeHeader.csv,/bytecodedl/output/.*CallEdge.csv" --nodes="/bytecodedl/neo4j/CallNodeHeader.csv,/bytecodedl/output/.*CallNode.csv" --delimiter="\t" $dbname + +if grep -q "dbms.active_database" /var/lib/neo4j/conf/neo4j.conf; then + sed -i -E "s/dbms.active_database=\w+/dbms.active_database=$dbname/g" /var/lib/neo4j/conf/neo4j.conf +else + echo "dbms.active_database=$dbname" >> /var/lib/neo4j/conf/neo4j.conf +fi diff --git a/neoImportCall.sh b/neoImportCall.sh index da1e711..7e2ae73 100644 --- a/neoImportCall.sh +++ b/neoImportCall.sh @@ -2,10 +2,10 @@ dbname=$1$(date "+%m%d%H%M") -neo4j-admin import --relationships=Call="/bytecodedl/neo4j/CallEdgeHeader.csv,/bytecodedl/output/.*CallEdge.csv" --nodes="/bytecodedl/neo4j/CallNodeHeader.csv,/bytecodedl/output/.*CallNode.csv" --database=$dbname --delimiter="\t" +neo4j-admin database import full --nodes="/bytecodedl/neo4j/CallNodeHeader.csv,/bytecodedl/output/.*CallNode.csv" --relationships=Call="/bytecodedl/neo4j/CallEdgeHeader.csv,/bytecodedl/output/CallEdge.csv" --delimiter="\t" $dbname -if grep -q "dbms.active_database" /var/lib/neo4j/conf/neo4j.conf; then - sed -i -E "s/dbms.active_database=\w+/dbms.active_database=$dbname/g" /var/lib/neo4j/conf/neo4j.conf +if grep -q "#initial.dbms.default_database" /var/lib/neo4j/conf/neo4j.conf; then + sed -i -E "s/#initial.dbms.default_database=\S+/initial.dbms.default_database=$dbname/g" /var/lib/neo4j/conf/neo4j.conf else - echo "dbms.active_database=$dbname" >> /var/lib/neo4j/conf/neo4j.conf -fi \ No newline at end of file + sed -i -E "s/initial.dbms.default_database=\S+/initial.dbms.default_database=$dbname/g" /var/lib/neo4j/conf/neo4j.conf +fi diff --git a/neoImportChaCall-4.4.sh b/neoImportChaCall-4.4.sh new file mode 100644 index 0000000..ef763e4 --- /dev/null +++ b/neoImportChaCall-4.4.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +dbname=$1$(date "+%m%d%H%M") + +neo4j-admin database import --nodes=Method="/bytecodedl/neo4j/CallNodeHeader.csv,/bytecodedl/output/.*CallNode.csv" --relationships=Call="/bytecodedl/neo4j/CallEdgeHeader.csv,/bytecodedl/output/CallEdge.csv" --relationships=Cha="/bytecodedl/neo4j/ChaEdgeHeader.csv,/bytecodedl/output/ChaGraph.csv" --database=$dbname --delimiter="\t" + +if grep -q "dbms.active_database" /var/lib/neo4j/conf/neo4j.conf; then + sed -i -E "s/dbms.active_database=\w+/dbms.active_database=$dbname/g" /var/lib/neo4j/conf/neo4j.conf +else + echo "dbms.active_database=$dbname" >> /var/lib/neo4j/conf/neo4j.conf +fi diff --git a/neoImportChaCall.sh b/neoImportChaCall.sh index d1445e9..130c7bb 100644 --- a/neoImportChaCall.sh +++ b/neoImportChaCall.sh @@ -2,10 +2,10 @@ dbname=$1$(date "+%m%d%H%M") -neo4j-admin import --nodes=Method="/bytecodedl/neo4j/CallNodeHeader.csv,/bytecodedl/output/.*CallNode.csv" --relationships=Call="/bytecodedl/neo4j/CallEdgeHeader.csv,/bytecodedl/output/CallEdge.csv" --relationships=Cha="/bytecodedl/neo4j/ChaEdgeHeader.csv,/bytecodedl/output/ChaGraph.csv" --database=$dbname --delimiter="\t" +neo4j-admin database import full --nodes=Method="/bytecodedl/neo4j/CallNodeHeader.csv,/bytecodedl/output/.*CallNode.csv" --relationships=Call="/bytecodedl/neo4j/CallEdgeHeader.csv,/bytecodedl/output/CallEdge.csv" --relationships=Cha="/bytecodedl/neo4j/ChaEdgeHeader.csv,/bytecodedl/output/ChaEdge.csv" --delimiter="\t" $dbname -if grep -q "dbms.active_database" /var/lib/neo4j/conf/neo4j.conf; then - sed -i -E "s/dbms.active_database=\w+/dbms.active_database=$dbname/g" /var/lib/neo4j/conf/neo4j.conf +if grep -q "#initial.dbms.default_database" /var/lib/neo4j/conf/neo4j.conf; then + sed -i -E "s/#initial.dbms.default_database=\w+/initial.dbms.default_database=$dbname/g" /var/lib/neo4j/conf/neo4j.conf else - echo "dbms.active_database=$dbname" >> /var/lib/neo4j/conf/neo4j.conf -fi \ No newline at end of file + sed -i -E "s/initial.dbms.default_database=\w+/initial.dbms.default_database=$dbname/g" /var/lib/neo4j/conf/neo4j.conf +fi From 85e4bb7d09a5b2efc2941a91c464ad27034eeac7 Mon Sep 17 00:00:00 2001 From: yxxx Date: Thu, 28 Dec 2023 19:19:23 +0800 Subject: [PATCH 34/36] update neo4j docker image --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index e21273c..8f8e453 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,7 +7,7 @@ services: volumes: - ./:/bytecodedl neo: - image: neo4j-server:5.12.0-bytecodedl-pathfinder-1.0.0 + image: wuxxxxx/neo4j-server:5.12.0-bytecodedl-pathfinder-1.0.0 restart: always ports: - "0.0.0.0:7474:7474" From a5be2913ac971fd5699a8c03c100589b2e1eecb5 Mon Sep 17 00:00:00 2001 From: yxxx Date: Sat, 30 Dec 2023 10:10:15 +0800 Subject: [PATCH 35/36] update neo4j image name --- docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 8f8e453..5a834cf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,13 +1,13 @@ version: '2.4' services: bytecodedl: - image: wuxxxxx/bytecodedl:1.0.0 + image: wuxxxxx/bytecodedl:1.0.1 restart: always command: sleep infinity volumes: - ./:/bytecodedl neo: - image: wuxxxxx/neo4j-server:5.12.0-bytecodedl-pathfinder-1.0.0 + image: wuxxxxx/neo4j-server:5.12.0-bytecodedl-pathfinder-1.0.1 restart: always ports: - "0.0.0.0:7474:7474" From d7a833973cc8d68c944288270b6a50092c65f98e Mon Sep 17 00:00:00 2001 From: yxxx Date: Sat, 6 Jan 2024 16:53:02 +0800 Subject: [PATCH 36/36] update bytecodedl docker --- docker-compose.yml | 2 +- docker/bytecodedl/Dockerfile | 16 ++++++++++++++++ docker/neo4j-server/Dockerfile | 9 +++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 docker/bytecodedl/Dockerfile create mode 100644 docker/neo4j-server/Dockerfile diff --git a/docker-compose.yml b/docker-compose.yml index 5a834cf..e4435eb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '2.4' services: bytecodedl: - image: wuxxxxx/bytecodedl:1.0.1 + image: wuxxxxx/bytecodedl:1.0.2 restart: always command: sleep infinity volumes: diff --git a/docker/bytecodedl/Dockerfile b/docker/bytecodedl/Dockerfile new file mode 100644 index 0000000..b5e456a --- /dev/null +++ b/docker/bytecodedl/Dockerfile @@ -0,0 +1,16 @@ +From ubuntu:22.04 + +LABEL version="1.0.2" +LABEL maintainer="yxxx " + +RUN apt-get update \ + && apt-get install -y vim \ + && apt-get install -y wget \ + && apt-get install -y git \ + && apt-get install -y openjdk-8-jdk + +RUN wget https://souffle-lang.github.io/ppa/souffle-key.public -O /usr/share/keyrings/souffle-archive-keyring.gpg \ + && echo "deb [signed-by=/usr/share/keyrings/souffle-archive-keyring.gpg] https://souffle-lang.github.io/ppa/ubuntu/ stable main" | tee /etc/apt/sources.list.d/souffle.list \ + && apt update && apt install -y souffle + +RUN wget https://github.com/BytecodeDL/soot-fact-generator/releases/download/v1.4.2/soot-fact-generator-1.4.2.jar \ No newline at end of file diff --git a/docker/neo4j-server/Dockerfile b/docker/neo4j-server/Dockerfile new file mode 100644 index 0000000..303c642 --- /dev/null +++ b/docker/neo4j-server/Dockerfile @@ -0,0 +1,9 @@ +From neo4j:5.12.0 + +LABEL version="1.0.1" +LABEL maintainer="yxxx " + +RUN wget https://github.com/BytecodeDL/bytecodedl-pathfinder-neo4j-procedure/releases/download/v1.0.1/bytecodedl-pathfinder-1.0.1.jar -O /var/lib/neo4j/plugins/bytecodedl-pathfinder-1.0.1.jar + +ENV NEO4J_AUTH=neo4j/bytecodedl \ + NEO4J_dbms_security_procedures_unrestricted=bytecodedl.* \ No newline at end of file