├── .gitignore ├── README.md ├── TaintWrapperSource.txt ├── Test ├── Test.iml └── src │ ├── Animal.java │ ├── Book.java │ ├── Car.java │ ├── Cat.java │ ├── ComplexContextSensitivityTest.java │ ├── ConditionalFlowTest.java │ ├── ContextSensitivityTest.java │ ├── InterTaintTest.java │ ├── MainClass.java │ ├── NestedFieldTest.java │ ├── RecursiveCallTest.java │ ├── SimpleInterAnalysisTest.java │ ├── SimpleIntraAnalysisTest.java │ ├── TaintWrapperTest.java │ └── Vehicle.java ├── app └── README.md ├── archive ├── ccc.md ├── checking │ ├── CaseSensitivityChk.java │ ├── CheckPass.java │ ├── DataTypeChk.java │ ├── DefaultValueChk.java │ └── UnusedParamChk.java ├── config_files │ ├── alluxio-default.xml │ ├── core-default.xml │ ├── hbase-default.xml │ ├── hdfs-default.xml │ ├── mapred-default.xml │ ├── yarn-default.xml │ └── zookeeper-default.xml └── notes │ ├── caseSensitivityCheck.md │ └── caseSensitivityCheckPic │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ └── 6.png ├── doc ├── cflow.jpg └── cflow_report.pdf ├── pom.xml ├── run.sh └── src ├── main ├── java │ ├── Main.java │ ├── assertion │ │ └── Assert.java │ ├── configInterface │ │ ├── ConfigInterface.java │ │ ├── HadoopInterface.java │ │ ├── SparkInterface.java │ │ └── TestInterface.java │ ├── taintAnalysis │ │ ├── InterAnalysisTransformer.java │ │ ├── InterTaintAnalysis.java │ │ ├── IntraAnalysisTransformer.java │ │ ├── PathVisitor.java │ │ ├── SourceSinkConnectionVisitor.java │ │ ├── Taint.java │ │ ├── TaintAnalysisDriver.java │ │ ├── TaintFlowAnalysis.java │ │ ├── sourceSinkManager │ │ │ ├── ISourceSinkManager.java │ │ │ └── SourceSinkManager.java │ │ ├── taintWrapper │ │ │ ├── ITaintWrapper.java │ │ │ └── TaintWrapper.java │ │ └── utility │ │ │ ├── PhantomIdentityStmt.java │ │ │ └── PhantomRetStmt.java │ └── utility │ │ └── Config.java └── resources │ └── simplelogger.properties └── test └── java └── taintAnalysisTest ├── HadoopInterAnalysisTest.java ├── HadoopIntraAnalysisTest.java ├── InterAnalysisTest.java ├── SimpleIntraAnalysisTest.java └── TaintAnalysisTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | 25 | # IDE-related files 26 | .idea/ 27 | .settings/ 28 | target/ 29 | .classpath 30 | .project 31 | ccc.iml 32 | 33 | # app 34 | app/hadoop*/ 35 | app/spark*/ 36 | app/hbase*/ 37 | 38 | # temp files 39 | tmp1.txt 40 | 41 | # vscode 42 | .vscode/ 43 | 44 | # sootOutput 45 | sootOutput/ 46 | 47 | # Test 48 | *.txt 49 | *.class 50 | *.jar 51 | .idea 52 | resource/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cFlow: A Flow-based Configuration Analysis Framework 2 | 3 | cFlow is a flow-, field-, and context-sensitive static taint analysis framework for Java bytecode based clooud systems that tracks how configuration option values flow through a program from their loading points to the user-specified sink points where the values are used (e.g. an external API call), and can output the taint propagation path from the source to the sink. It could also be used as a generic static taint anaysis tool by providing your own definition of sources and sinks. 4 | 5 | ## Use cFlow from the command line 6 | 7 | S1. Download the software you want to analyze and put it under `/app`. Currently supported cloud systems include: hdfs, mapreduce, yarn, hadoop_common, hadoop_tools, hbase, spark. 8 | 9 | S2. Compile cFlow 10 | 11 | ```sh 12 | mvn compile 13 | ``` 14 | 15 | S3. Run the analysis. 16 | 17 | ```sh 18 | ./run.sh -a hadoop_common [-i] [-s] 19 | ``` 20 | 21 | If the `-i` flag is enabled, only intra-procedural analysis in performed, used for testing only. 22 | 23 | If the `-s` flag is enabled, the SPARK call graph toolkit is used to compute a more accurate call graph at the cost of longer running time and higher memory consumption. 24 | 25 | S4. Inspect the result. 26 | 27 | A `tmp.txt` file will be generated, which contains all the discovered taint propagation paths from sources to sinks. 28 | 29 | ## Use cFlow as a library 30 | 31 | The following code piece illustrates how to use cFlow as a library. You may also want to refer to `Main.java`. 32 | 33 | ```java 34 | // The configuration for the analyzing the software, which is predefined in Config.java 35 | String[] cfg = ...; 36 | 37 | 38 | // srcPaths is a list of string paths to the jars file of the core part of the software (usu. excluding library code) 39 | List srcPaths = Config.getSourcePaths(cfg)); 40 | 41 | // classPaths is a list of string paths to the jars file of the software (including library code) 42 | List classPaths = Config.getClassPaths(cfg)); 43 | 44 | // Create an instance of ConfigInterace (specifies how to identify configuration loading/setting points) 45 | ConfigInterface configInterface = Config.getInterface(cfg); 46 | 47 | // Create an instance of SourceSinkManager (specifes the taint sources and sinks) 48 | ISourceSinkManager sourceSinkManager = new SourceSinkManager(configInterface); 49 | 50 | // Create an instance of TaintWrapper (used for library modeling) 51 | ITaintWrapper taintWrapper = TaintWrapper.getDefault(); 52 | 53 | 54 | /* Run analysis */ 55 | TaintAnalysisDriver driver = new TaintAnalysisDriver(sourceSinkManager, taintWrapper); 56 | 57 | // Run intra-procedural analysis 58 | IntraAnalysisTransformer intraTransformer = 59 | driver.runIntraTaintAnalysis(srcPaths, classPaths); 60 | 61 | // Run inter-procedural analysis 62 | // if use_spark is set to true, use the SPARK call graph toolkit for computing the call graph 63 | InterAnalysisTransformer interTransformer = 64 | driver.runInterTaintAnalysis(srcPaths, classPaths, use_spark); 65 | 66 | // Get the results of the inter-procedural analysis: 67 | // The key of the map is source taint. 68 | // List represents a taint propagation path. 69 | // The value of the map is a list of taint propagation paths between the source and a sink. 70 | Map>> results = interTransformer.getPathsMap(); 71 | ``` 72 | 73 | ## The software I want to analyze is not supported 74 | 75 | S1. Download the software and put it under `\app` as usual. 76 | 77 | S2. Extend `taintAnalysis/utility/Config.java` to specify where to load the source code of the analyzed software. Two path needs to specified: 78 | 79 | * Source Path: The path to the core of the analyzed software, excluding library code (unless you want cFlow to look into the library codebase for full-scale analysis). 80 | 81 | * Class Path: The path to both the core of the software and its dependencies. 82 | 83 | S3. (Optional) Implement the `ConfigInterface` interface in the `configInterface` package to specify how to identify the configuration loading/setting points. 84 | 85 | S4. (Optional) Implement the `ISourceSinkManager` interface in the `taintAnalysis/sourceSinkManager` subpackage to specify the sources and sinks used for the analysis. 86 | 87 | Now, you should be good to go. 88 | 89 | ## Code structure 90 | 91 | The architecture of cFlow is as follows: 92 | 93 | ![142](doc/cflow.jpg) 94 | 95 | The source code of cFlow is placed under the `src/main/java` directory. 96 | 97 | It contains the following main modules: 98 | 99 | * `configInterface` package contains the configuration interface used to identify configuration loading/setting points; implementations for several cloud systems have already been provided. 100 | * `taintAnalysis` package contains the core module for static taint analysis: 101 | * `sourceSinkManger` subpackage contains the interface for defining sources/sinks and a default implementation of it; 102 | * `taintWrapper` subpackage contains the interface for defining library modeling rules and a default implementation using herustic-based rules. 103 | * `Taint.java` contains the implementation of the field-sensitive taint abstraction. 104 | * `TaintFlowAnalysis.java` contains the implementaition for intra-procedural taint analysis, which extends the `ForwardFlowAnalysis` class in Soot. 105 | * `InterTaintAnalysis.java` contains the implmentation for inter-procedural taint analysis. 106 | * `PathVisitor.java` contains the implementation of a recursive realizable path visitor that traverses all the realizable taint propagation paths starting from a source taint. Mainly used for validation, and not enabled by default. 107 | * `SourceSinkConnectionVisitor.java` containts the the implementation of a recursive realizable path reconstructor that reconstructs the realizable taint propagation paths between any source sink pair. 108 | * `utility` package contains `Config.java`, which specifies where to load the source code of the analyzed software. If the analyzed software is not supported by default, you should extend this file. 109 | 110 | ## Documents 111 | 112 | For more details of the design and implementation of cFlow: 113 | 114 | * [Report](doc/cflow_report.pdf) 115 | 116 | * [Design Slide](https://docs.google.com/presentation/d/1XluXB7bBepI0bVzGl3IhC9ecMd1SiP1sxXrHQZax10o/edit?usp=sharing) 117 | 118 | ## Resources 119 | 120 | * [Soot Tutorial](http://www.iro.umontreal.ca/~dufour/cours/ift6315/docs/soot-tutorial.pdf) 121 | -------------------------------------------------------------------------------- /Test/Test.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Test/src/Animal.java: -------------------------------------------------------------------------------- 1 | public interface Animal { 2 | 3 | void sleep(); 4 | 5 | void eat(); 6 | 7 | void makeSound(); 8 | 9 | void dynamicBinding(Animal a); 10 | 11 | } -------------------------------------------------------------------------------- /Test/src/Book.java: -------------------------------------------------------------------------------- 1 | public class Book { 2 | int a; 3 | int b; 4 | 5 | Book() { 6 | a = 0; 7 | b = 0; 8 | } 9 | 10 | Book(int a_, int b_) { 11 | a = a_; 12 | b = b_; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Test/src/Car.java: -------------------------------------------------------------------------------- 1 | public class Car extends Vehicle { 2 | 3 | int b; 4 | 5 | Car() { 6 | super(); 7 | b = 1; 8 | } 9 | 10 | @Override 11 | public void dynamicBinding1(Vehicle v) { 12 | a = v.a + source(); 13 | } 14 | 15 | @Override 16 | public void dynamicBinding2(Vehicle v) { 17 | a = 2; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Test/src/Cat.java: -------------------------------------------------------------------------------- 1 | public class Cat implements Animal { 2 | 3 | int a; 4 | String b; 5 | 6 | Cat() { 7 | a = 10; 8 | } 9 | 10 | int source() { 11 | return 1; 12 | } 13 | 14 | @Override 15 | public void sleep() { 16 | a = source(); 17 | } 18 | 19 | @Override 20 | public void eat() { 21 | System.out.println("The cat eats."); 22 | } 23 | 24 | @Override 25 | public void makeSound() { 26 | System.out.println("The cat meows."); 27 | } 28 | 29 | @Override 30 | public void dynamicBinding(Animal cat) { 31 | Cat c = (Cat) cat; 32 | a = c.a + source(); 33 | } 34 | } -------------------------------------------------------------------------------- /Test/src/ComplexContextSensitivityTest.java: -------------------------------------------------------------------------------- 1 | public class ComplexContextSensitivityTest { 2 | 3 | private int source() { 4 | return 7; 5 | } 6 | 7 | public void run() { 8 | int s = source(); 9 | A1(s); 10 | A2(s); 11 | D1(s); 12 | D2(s); 13 | } 14 | 15 | private void A1(int s) { 16 | int c = B1(s); 17 | System.out.println(c); 18 | } 19 | 20 | private void A2(int s) { 21 | int c = B1(s); 22 | int b = c + 10; 23 | System.out.println(c); 24 | } 25 | 26 | private int B1(int s) { 27 | int c = C(s); 28 | System.out.println(c); 29 | return c; 30 | } 31 | 32 | private int B2(int s) { 33 | int c = C(s); 34 | System.out.println(c); 35 | return c; 36 | } 37 | 38 | private void D1(int s) { 39 | int c = B2(s); 40 | System.out.println(c); 41 | } 42 | 43 | private void D2(int s) { 44 | int c = B2(s); 45 | System.out.println(c); 46 | } 47 | 48 | private int C(int i) { 49 | int ret = i + 10; 50 | return ret; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Test/src/ConditionalFlowTest.java: -------------------------------------------------------------------------------- 1 | public class ConditionalFlowTest { 2 | 3 | private int source() { 4 | return 7; 5 | } 6 | 7 | public void run() { 8 | test(); 9 | } 10 | 11 | private void test() { 12 | int s = source(); 13 | if (s > 10) { 14 | System.out.println(s); 15 | } else { 16 | System.out.println(s + 1); 17 | } 18 | int c = s + 10; 19 | System.out.println(c); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /Test/src/ContextSensitivityTest.java: -------------------------------------------------------------------------------- 1 | public class ContextSensitivityTest { 2 | 3 | private int source() { 4 | return 7; 5 | } 6 | 7 | public void run() { 8 | int s = source(); 9 | A(s); 10 | B(s); 11 | D(s); 12 | } 13 | 14 | private void A(int s) { 15 | int c = C(s); 16 | System.out.println(c); 17 | } 18 | 19 | private void B(int s) { 20 | int c = C(s); 21 | System.out.println(c); 22 | } 23 | 24 | private void D(int s) { 25 | int c = C(s); 26 | System.out.println(c); 27 | } 28 | 29 | private int C(int i) { 30 | int ret = i + 10; 31 | return ret; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Test/src/InterTaintTest.java: -------------------------------------------------------------------------------- 1 | public class InterTaintTest { 2 | 3 | private int source() { 4 | return 7; 5 | } 6 | 7 | public InterTaintTest() { 8 | 9 | } 10 | 11 | public void testInheritance() { 12 | Vehicle v1 = new Car(); 13 | Vehicle v2 = new Car(); 14 | v1.dynamicBinding1(v2); 15 | v1.dynamicBinding2(v2); 16 | } 17 | 18 | public void testInterface() { 19 | Cat cat1 = new Cat(); 20 | Cat cat2 = new Cat(); 21 | cat1.dynamicBinding(cat2); 22 | } 23 | } -------------------------------------------------------------------------------- /Test/src/MainClass.java: -------------------------------------------------------------------------------- 1 | public class MainClass { 2 | 3 | public static void main(String[] args) { 4 | // Test basics of intra-procedural analysis 5 | SimpleIntraAnalysisTest simpleIntraAnalysisTest = new SimpleIntraAnalysisTest(); 6 | simpleIntraAnalysisTest.run(); 7 | 8 | // Test basics of inter-procedural analysis 9 | SimpleInterAnalysisTest simpleInterAnalysisTest = new SimpleInterAnalysisTest(); 10 | simpleInterAnalysisTest.run(); 11 | 12 | // Test behaviors related to dynamic binding 13 | InterTaintTest main2 = new InterTaintTest(); 14 | main2.testInheritance(); 15 | main2.testInterface(); 16 | 17 | // Test nested field access 18 | NestedFieldTest nestedFieldTest = new NestedFieldTest(); 19 | nestedFieldTest.run(); 20 | 21 | // Test recursive call 22 | RecursiveCallTest recursiveCallTest = new RecursiveCallTest(); 23 | recursiveCallTest.run(); 24 | 25 | // Test context sensitive path building 26 | ContextSensitivityTest contextSensitivityTest = new ContextSensitivityTest(); 27 | contextSensitivityTest.run(); 28 | 29 | ComplexContextSensitivityTest complexContextSensitivityTest = new ComplexContextSensitivityTest(); 30 | complexContextSensitivityTest.run(); 31 | 32 | // Test taint wrapper for common external API calls 33 | TaintWrapperTest taintWrapperTest = new TaintWrapperTest(); 34 | taintWrapperTest.run(); 35 | 36 | ConditionalFlowTest conditionalFlowTest = new ConditionalFlowTest(); 37 | conditionalFlowTest.run(); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /Test/src/NestedFieldTest.java: -------------------------------------------------------------------------------- 1 | public class NestedFieldTest { 2 | 3 | Book book; 4 | 5 | private int source() { 6 | return 7; 7 | } 8 | 9 | public void run() { 10 | book = new Book(); 11 | book.a = source(); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /Test/src/RecursiveCallTest.java: -------------------------------------------------------------------------------- 1 | public class RecursiveCallTest { 2 | 3 | private int source() { 4 | return 7; 5 | } 6 | 7 | public void run() { 8 | int res = fac(source()); 9 | System.out.println(res); 10 | } 11 | 12 | private int fac(int i) { 13 | if (i == 0) { 14 | return 1; 15 | } 16 | return i * fac(i - 1); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /Test/src/SimpleInterAnalysisTest.java: -------------------------------------------------------------------------------- 1 | public class SimpleInterAnalysisTest { 2 | 3 | int i1; 4 | int i2; 5 | 6 | public void run() { 7 | // Test tainting base 8 | test1(); 9 | 10 | // Test tainting return value 11 | int ret = test2(); 12 | System.out.println(ret); 13 | 14 | // Test tainting parameters 15 | int a = source(); 16 | int b = 5; 17 | Book book = new Book(); 18 | test3(a, b, book); 19 | } 20 | 21 | private int source() { 22 | return 7; 23 | } 24 | 25 | public void test1() { 26 | i1 = source(); 27 | i2 = i1 + 10; 28 | System.out.println(i2); 29 | } 30 | 31 | public int test2() { 32 | int a = source(); 33 | int b = a + 10; 34 | return b; 35 | } 36 | 37 | private void test3(int a, int b, Book book) { 38 | book.a = source(); // taint shall be retained after exiting the method 39 | a = 5; // shall not kill killed the taint on a outside the method 40 | b = source(); // taint shall be killed after exiting the method 41 | System.out.println(a); 42 | System.out.println(b); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /Test/src/SimpleIntraAnalysisTest.java: -------------------------------------------------------------------------------- 1 | public class SimpleIntraAnalysisTest { 2 | 3 | int i1; 4 | int i2; 5 | 6 | public void run() { 7 | test1(); 8 | test2(); 9 | test3(); 10 | test4(); 11 | test5(); 12 | test6(); 13 | test7(); 14 | test8(); 15 | test10(); 16 | } 17 | 18 | private int source() { 19 | return 7; 20 | } 21 | 22 | private void callee(Book b1, Book b2, int v) { 23 | if (v > 1) { 24 | v = source(); // v shouldn't be 25 | b2.a = source(); 26 | i1 = b2.a; 27 | return; 28 | } else { 29 | v = source(); 30 | b1.a = source(); 31 | i1 = b1.a; 32 | return; 33 | } 34 | } 35 | 36 | public Book callee2(Book b1, Book b2, int v) { 37 | Book book = new Book(); 38 | if (v > 1) { 39 | b2.a = source(); 40 | book.a = b2.a; 41 | } else { 42 | b1.a = source(); 43 | book.a = b1.a; 44 | } 45 | 46 | return book; 47 | } 48 | 49 | public void test1() { 50 | // Trivial case 51 | int a = source(); 52 | int b = a + 10; 53 | System.out.println(b); 54 | } 55 | 56 | public void test2() { 57 | // If Branches 58 | int a = source(); 59 | int b; 60 | if (a > 10) { 61 | b = 7; 62 | } else { 63 | b = a + 10; 64 | } 65 | int c = b + 20; // c should be tainted 66 | System.out.println(c); 67 | } 68 | 69 | public void test3() { 70 | // Loops 71 | int a = source(); 72 | int b = 0; 73 | for (int i = 0; i < 10; i++) { 74 | b = a + 10; 75 | b = b + 15; 76 | } 77 | int c = b + 20; 78 | System.out.println(c); 79 | } 80 | 81 | public void test4() { 82 | // Kill Taint 83 | int a = source(); 84 | a = 20; // a's taint is killed 85 | int b = a; // b should not be tainted 86 | System.out.println(b); 87 | } 88 | 89 | public void test5() { 90 | // Fields 1 91 | i1 = source(); 92 | i2 = i1 + 10; 93 | System.out.println(i2); 94 | } 95 | 96 | public void test6() { 97 | // Fields 2 98 | Book book1 = new Book(); 99 | Book book2 = new Book(); 100 | book1.a = source(); 101 | book2 = book1; 102 | i1 = book2.a; // i1 should be tainted 103 | System.out.println(i1); 104 | } 105 | 106 | public void test7() { 107 | // Fields 3 108 | Book book1 = new Book(); 109 | Book book2 = new Book(); 110 | book1.a = source(); 111 | book1 = book2; 112 | i1 = book1.a; // i1 should not be tainted 113 | System.out.println(i1); 114 | } 115 | 116 | public void test8() { 117 | // Multiple sources 118 | int a = source(); 119 | int b = source(); 120 | System.out.println(a); 121 | System.out.println(b); 122 | } 123 | 124 | public void test10() { 125 | // Test visitReturn and visitReturnVoid 126 | Book book1 = new Book(); 127 | Book book2 = new Book(); 128 | int dummy = 0; 129 | callee(book1, book2, dummy); 130 | Book book3 = callee2(book1, book2, dummy); 131 | i1 = book1.a; 132 | System.out.println(i1); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /Test/src/TaintWrapperTest.java: -------------------------------------------------------------------------------- 1 | import java.util.ArrayList; 2 | import java.util.List; 3 | 4 | public class TaintWrapperTest { 5 | 6 | private int source(int base) { return 7 + base; } 7 | 8 | private String source() { 9 | return "7"; 10 | } 11 | 12 | public void run() { 13 | test1(); 14 | test2(); 15 | test3(); 16 | test4(); 17 | test5(); 18 | test6(); 19 | } 20 | 21 | // Taint both 22 | private void test1() { 23 | String s = source(); 24 | StringBuilder str = new StringBuilder(s); 25 | str.append("lucky"); 26 | System.out.println(str); 27 | } 28 | 29 | // Taint base 30 | private void test2() { 31 | List lst = new ArrayList<>(); 32 | lst.add(source()); 33 | String s = lst.get(0); 34 | System.out.println(s); 35 | } 36 | 37 | // Taint return 38 | private void test3() { 39 | int a = source(7); 40 | int max = Math.max(a, 5); 41 | System.out.println(max); 42 | } 43 | 44 | // Exclude 45 | private void test4() { 46 | List lst = new ArrayList<>(); 47 | lst.add(source()); 48 | int len = lst.size(); 49 | System.out.println(len); 50 | } 51 | 52 | // Kill Taint 53 | private void test5() { 54 | List lst = new ArrayList<>(); 55 | lst.add(source()); 56 | lst.clear(); 57 | lst.add("1"); 58 | String s = lst.get(0); 59 | System.out.println(s); 60 | } 61 | 62 | // Not registered 63 | private void test6() { 64 | // TODO 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /Test/src/Vehicle.java: -------------------------------------------------------------------------------- 1 | public class Vehicle { 2 | 3 | int a; 4 | 5 | Vehicle() { 6 | a = source(); 7 | } 8 | 9 | Vehicle(int a_) { 10 | a = a_; 11 | } 12 | 13 | int source() { 14 | return 7; 15 | } 16 | 17 | public void dynamicBinding1(Vehicle v) { 18 | a = v.a; 19 | } 20 | 21 | public void dynamicBinding2(Vehicle v) { 22 | a = source() + 1; 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /app/README.md: -------------------------------------------------------------------------------- 1 | Download `hadoop-3.3.0.tar.gz` from https://archive.apache.org/dist/hadoop/common/hadoop-3.3.0/hadoop-3.3.0.tar.gz 2 | 3 | Download `hbase-2.3.1-bin.tar.gz` from https://archive.apache.org/dist/hbase/2.3.1/hbase-2.3.1-bin.tar.gz 4 | 5 | Download `alluxio-1.8.0` from https://www.alluxio.io/download/ 6 | 7 | Download `apache-zookeeper-3.5.6-bin` from https://archive.apache.org/dist/zookeeper/zookeeper-3.5.6/apache-zookeeper-3.5.6-bin.tar.gz 8 | 9 | Download `spark-2.4.6-bin-hadoop2.7` from https://archive.apache.org/dist/spark/spark-2.4.6/spark-2.4.6-bin-hadoop2.7.tgz 10 | -------------------------------------------------------------------------------- /archive/ccc.md: -------------------------------------------------------------------------------- 1 | # ccc 2 | 3 | Configuration Consistency Checker 4 | 5 | ## Project Docs 6 | 7 | * [Project Proposal](https://docs.google.com/document/d/1nyOCS7g5iyMzjjQasGd7yhnjIIlhQ9X2ivUStXT_N-k/edit?usp=sharing) 8 | * First [issue](https://github.com/xlab-uiuc/ccc/issues/1) in this repo on Project Scope and Approach 9 | 10 | ## Consistency Checking 11 | 12 | * [Common Properties Doc](https://docs.google.com/document/d/1d-FnKT3N6oEbi8nm-2HJEtfbhUiXxWYeiq36_nnrXCg/edit?usp=sharing) 13 | * [Inconsistencies found](https://docs.google.com/document/d/1fQHlm-B35eHaKczrnIS9FRXRKOyOKJvCCVDcuunWh-k/edit) 14 | -------------------------------------------------------------------------------- /archive/checking/CaseSensitivityChk.java: -------------------------------------------------------------------------------- 1 | package checking; 2 | 3 | import acai.configInterface.ConfigInterface; 4 | import org.xml.sax.SAXException; 5 | import soot.Type; 6 | import soot.jimple.DefinitionStmt; 7 | import soot.jimple.Stmt; 8 | import soot.jimple.infoflow.results.InfoflowResults; 9 | import soot.jimple.infoflow.results.ResultSinkInfo; 10 | import soot.jimple.infoflow.results.ResultSourceInfo; 11 | 12 | import javax.xml.parsers.ParserConfigurationException; 13 | import java.io.IOException; 14 | import java.util.*; 15 | 16 | import acai.utility.Util; 17 | 18 | public class CaseSensitivityChk implements CheckPass{ 19 | 20 | // Case type of a string 21 | final int SOOT_TOKEN = 0; // tokens in soot such as $r1, r14. should not analyse their case type 22 | final int ALL_LOWER = 1; // AAA 23 | final int ALL_UPPER = 2; // aaa 24 | final int MIX = 3; // AAa 25 | 26 | private class CaseInfo{ 27 | CaseInfo() { 28 | strings = new ArrayList<>(); 29 | equalsExist = equalsIgnoreCaseExists = allLowerExists = allUpperExists = mixExists = false; 30 | } 31 | List strings; // all the args of "equals" or "equalsIgnoreCase", used for log 32 | boolean equalsExist; 33 | boolean equalsIgnoreCaseExists; 34 | boolean allUpperExists; 35 | boolean allLowerExists; 36 | boolean mixExists; 37 | } 38 | 39 | @Override 40 | public void runChecking(ConfigInterface configInterface, InfoflowResults results, String[][] considered) { 41 | Map mp = new HashMap<>(); 42 | for (ResultSinkInfo sink : results.getResults().keySet()) { 43 | for (ResultSourceInfo source : results.getResults().get(sink)) { 44 | Stmt[] path = source.getPath(); 45 | 46 | if (path == null) continue; 47 | Stmt firstStmt = path[0]; 48 | 49 | if (!(firstStmt instanceof DefinitionStmt)) continue; 50 | DefinitionStmt definitionFirstStmt = (DefinitionStmt) path[0]; 51 | 52 | Type type = definitionFirstStmt.getLeftOp().getType(); 53 | if (!type.toString().equals("java.lang.String")) continue; // No need to check case sensitivity of non-String type params 54 | 55 | //String paramName = definitionFirstStmt.getInvokeExpr().getArg(0).toString(); 56 | String paramName = configInterface.getConfigName(definitionFirstStmt.getInvokeExpr()); 57 | 58 | // ignore parameters whose flowdroid results are inconsistent 59 | Set affectedParams = Util.getAffectedParams(considered); 60 | if (affectedParams.contains(paramName)) continue; 61 | 62 | for (Stmt stmt: path){ 63 | try { 64 | DefinitionStmt definitionStmt = (DefinitionStmt) stmt; 65 | String methodName = definitionStmt.getInvokeExpr().getMethod().getName(); 66 | 67 | if (methodName.equals("equals")) { 68 | String arg = definitionStmt.getInvokeExpr().getArg(0).toString(); 69 | if (!mp.containsKey(paramName)){ 70 | CaseInfo caseInfo = new CaseInfo(); 71 | caseInfo.strings.add(arg + "(used in method \"equals\")"); 72 | caseInfo.equalsExist = true; 73 | mp.put(paramName, caseInfo); 74 | } else { 75 | mp.get(paramName).strings.add(arg + "(used in method \"equals\")"); 76 | mp.get(paramName).equalsExist = true; 77 | } 78 | int argCaseType = caseType(arg); 79 | switch (argCaseType){ 80 | case SOOT_TOKEN: 81 | break; 82 | case ALL_UPPER: 83 | mp.get(paramName).allUpperExists = true; 84 | break; 85 | case ALL_LOWER: 86 | mp.get(paramName).allLowerExists = true; 87 | break; 88 | case MIX: 89 | mp.get(paramName).mixExists = true; 90 | break; 91 | } 92 | 93 | } else if (methodName.equals("equalsIgnoreCase")){ 94 | String arg = definitionStmt.getInvokeExpr().getArg(0).toString(); 95 | if (!mp.containsKey(paramName)){ 96 | CaseInfo caseInfo = new CaseInfo(); 97 | caseInfo.strings.add(arg + "(used in method \"equalsIgnoreCase\")"); 98 | caseInfo.equalsIgnoreCaseExists = true; 99 | mp.put(paramName, caseInfo); 100 | } else { 101 | mp.get(paramName).strings.add(arg + "(used in method \"equalsIgnoreCase\")"); 102 | mp.get(paramName).equalsIgnoreCaseExists = true; 103 | } 104 | int argCaseType = caseType(arg); 105 | switch (argCaseType){ 106 | case SOOT_TOKEN: 107 | break; 108 | case ALL_UPPER: 109 | mp.get(paramName).allUpperExists = true; 110 | break; 111 | case ALL_LOWER: 112 | mp.get(paramName).allLowerExists = true; 113 | break; 114 | case MIX: 115 | mp.get(paramName).mixExists = true; 116 | break; 117 | } 118 | } 119 | } catch (Exception exception){ 120 | 121 | } 122 | } 123 | 124 | } 125 | } 126 | 127 | 128 | 129 | // log information 130 | System.out.println("===== log of case sensitivity check ====="); 131 | for (Map.Entry entry : mp.entrySet()) { 132 | String key = entry.getKey(); 133 | CaseInfo caseInfo = entry.getValue(); 134 | List list = caseInfo.strings; 135 | System.out.println(); 136 | System.out.println(key + " : "); 137 | for (String s : list){ 138 | System.out.println(s); 139 | } 140 | } 141 | System.out.println("===== end of log of case sensitivity check ====="); 142 | 143 | boolean findInconsistency = false; 144 | for (Map.Entry entry : mp.entrySet()){ 145 | String key = entry.getKey(); 146 | CaseInfo caseInfo = entry.getValue(); 147 | if (caseInfo.equalsExist && caseInfo.equalsIgnoreCaseExists) { 148 | System.out.println("Case sensitivity inconsistency warning: Both \"equals\" and \"equalsIgnoreCase\" exist " + 149 | "in the flow of param " + key); 150 | findInconsistency = true; 151 | } 152 | int a = caseInfo.allLowerExists ? 1 : 0; 153 | int b = caseInfo.allUpperExists ? 1 : 0; 154 | int c = caseInfo.mixExists ? 1 : 0; 155 | if (a + b + c > 1) { 156 | System.out.println("Case sensitivity inconsistency warning: More than one type of string case type (all uppercase, " + 157 | "all lowercase, mixture of uppercase and lowercase) exist in the flow of param " + key); 158 | findInconsistency = true; 159 | } 160 | } 161 | if (!findInconsistency) { 162 | System.out.println("Case sensitivity check passed"); 163 | } 164 | } 165 | 166 | private int caseType(String arg){ 167 | if ((arg.length() > 2 && arg.startsWith("$r") && '0' <= arg.charAt(2) && arg.charAt(2) <= '9') 168 | || (arg.length() > 1 && arg.startsWith("r") && '0' <= arg.charAt(1) && arg.charAt(1) <= '9')){ 169 | return SOOT_TOKEN; 170 | } 171 | boolean upperExists = false; 172 | boolean lowerExists = false; 173 | for (int i = 0;i < arg.length();i++){ 174 | if ('a' <= arg.charAt(i) && arg.charAt(i) <= 'z') { 175 | lowerExists = true; 176 | } 177 | if ('A' <= arg.charAt(i) && arg.charAt(i) <= 'Z') { 178 | upperExists = true; 179 | } 180 | } 181 | if (upperExists && !lowerExists) return ALL_UPPER; 182 | if (!upperExists && lowerExists) return ALL_LOWER; 183 | return MIX; 184 | } 185 | 186 | } 187 | -------------------------------------------------------------------------------- /archive/checking/CheckPass.java: -------------------------------------------------------------------------------- 1 | package checking; 2 | 3 | import acai.configInterface.ConfigInterface; 4 | import soot.jimple.infoflow.results.InfoflowResults; 5 | 6 | public interface CheckPass { 7 | 8 | void runChecking(ConfigInterface configInterface, InfoflowResults results, String[][] considered); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /archive/checking/DataTypeChk.java: -------------------------------------------------------------------------------- 1 | package checking; 2 | 3 | import acai.configInterface.ConfigInterface; 4 | import org.w3c.dom.Document; 5 | import org.w3c.dom.Node; 6 | import org.w3c.dom.NodeList; 7 | import org.xml.sax.SAXException; 8 | import soot.Type; 9 | import soot.jimple.DefinitionStmt; 10 | import soot.jimple.Stmt; 11 | import soot.jimple.infoflow.results.InfoflowResults; 12 | import soot.jimple.infoflow.results.ResultSinkInfo; 13 | import soot.jimple.infoflow.results.ResultSourceInfo; 14 | 15 | import javax.xml.parsers.DocumentBuilder; 16 | import javax.xml.parsers.DocumentBuilderFactory; 17 | import javax.xml.parsers.ParserConfigurationException; 18 | import java.io.File; 19 | import java.io.IOException; 20 | import java.sql.SQLSyntaxErrorException; 21 | import java.util.HashMap; 22 | import java.util.HashSet; 23 | import java.util.Set; 24 | 25 | public class DataTypeChk implements CheckPass { 26 | 27 | @Override 28 | public void runChecking(ConfigInterface configInterface, InfoflowResults results, String[][] considered) { 29 | HashMap> m = new HashMap<>(); 30 | for (ResultSinkInfo sink : results.getResults().keySet()) { 31 | for (ResultSourceInfo source : results.getResults().get(sink)) { 32 | Stmt[] path = source.getPath(); 33 | if (path != null) { 34 | Stmt sourceStmt = path[0]; 35 | String configName = configInterface.getConfigName(sourceStmt.getInvokeExpr()); 36 | Type tp = ((DefinitionStmt)sourceStmt).getLeftOp().getType(); 37 | if (!m.containsKey(configName)) { 38 | m.put(configName, new HashSet<>()); 39 | } 40 | m.get(configName).add(tp); 41 | } 42 | } 43 | } 44 | for (String s : m.keySet()) { 45 | System.out.println(s + " : " + m.get(s).toString()); 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /archive/checking/DefaultValueChk.java: -------------------------------------------------------------------------------- 1 | package checking; 2 | 3 | import acai.configInterface.ConfigInterface; 4 | import soot.Value; 5 | import soot.jimple.InvokeExpr; 6 | import soot.jimple.infoflow.results.InfoflowResults; 7 | import soot.jimple.infoflow.results.ResultSinkInfo; 8 | import soot.jimple.infoflow.results.ResultSourceInfo; 9 | 10 | import java.util.HashMap; 11 | import java.util.HashSet; 12 | import java.util.Map; 13 | import java.util.Set; 14 | 15 | public class DefaultValueChk implements CheckPass { 16 | 17 | @Override 18 | public void runChecking(ConfigInterface configInterface, InfoflowResults results, String[][] considered) { 19 | HashMap> defaultValueMap = new HashMap<>(); 20 | for (ResultSinkInfo sink : results.getResults().keySet()) { 21 | for (ResultSourceInfo source : results.getResults().get(sink)) { 22 | InvokeExpr sourceInvokeExpr = source.getStmt().getInvokeExpr(); 23 | String configName = configInterface.getConfigName(sourceInvokeExpr); 24 | Value defaultValue = configInterface.getDefaultValue(sourceInvokeExpr); 25 | if (!defaultValueMap.containsKey(configName)) { 26 | defaultValueMap.put(configName, new HashSet<>()); 27 | } 28 | if (defaultValue != null) { 29 | defaultValueMap.get(configName).add(defaultValue); 30 | } 31 | } 32 | } 33 | 34 | System.out.println("=======runDefaultValueChecking======="); 35 | int numParams = defaultValueMap.size(); 36 | int cnt = 0; 37 | for (Map.Entry> pair : defaultValueMap.entrySet()) { 38 | String configName = pair.getKey(); 39 | Set defaultValues = pair.getValue(); 40 | if (defaultValues.size() > 1) { 41 | cnt++; 42 | System.out.printf("Config %s has more than one default values:\n", configName); 43 | System.out.printf(" %s\n", defaultValues.toString()); 44 | } 45 | } 46 | System.out.printf("%d/%d configs have more than one default values.\n", cnt, numParams); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /archive/checking/UnusedParamChk.java: -------------------------------------------------------------------------------- 1 | package checking; 2 | 3 | import acai.configInterface.ConfigInterface; 4 | import org.w3c.dom.Document; 5 | import org.w3c.dom.Node; 6 | import org.w3c.dom.NodeList; 7 | import org.xml.sax.SAXException; 8 | import soot.jimple.Stmt; 9 | import soot.jimple.infoflow.results.InfoflowResults; 10 | import soot.jimple.infoflow.results.ResultSinkInfo; 11 | import soot.jimple.infoflow.results.ResultSourceInfo; 12 | 13 | import javax.xml.parsers.DocumentBuilder; 14 | import javax.xml.parsers.DocumentBuilderFactory; 15 | import javax.xml.parsers.ParserConfigurationException; 16 | import java.io.File; 17 | import java.io.IOException; 18 | import java.util.HashSet; 19 | import java.util.Set; 20 | 21 | public class UnusedParamChk implements CheckPass { 22 | 23 | @Override 24 | public void runChecking(ConfigInterface configInterface, InfoflowResults results, String[][] considered) { 25 | HashSet paramSet = new HashSet<>(); 26 | for (ResultSinkInfo sink : results.getResults().keySet()) { 27 | for (ResultSourceInfo source : results.getResults().get(sink)) { 28 | Stmt sourceStmt = source.getStmt(); 29 | String configName = configInterface.getConfigName(sourceStmt.getInvokeExpr()); 30 | paramSet.add(configName); 31 | } 32 | } 33 | 34 | Set defaultConfigParams = null; 35 | try { 36 | defaultConfigParams = parseDefaultConfig(); 37 | } catch (ParserConfigurationException e) { 38 | e.printStackTrace(); 39 | } catch (IOException e) { 40 | e.printStackTrace(); 41 | } catch (SAXException e) { 42 | e.printStackTrace(); 43 | } 44 | defaultConfigParams.removeAll(paramSet); 45 | System.out.println("#####\nunused default parameters"); 46 | for (String s : defaultConfigParams) { 47 | System.out.println(s); 48 | } 49 | } 50 | 51 | private Set parseDefaultConfig() throws ParserConfigurationException, IOException, SAXException { 52 | DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 53 | factory.setValidating(true); 54 | factory.setIgnoringElementContentWhitespace(true); 55 | DocumentBuilder builder = factory.newDocumentBuilder(); 56 | // TODO: hardcode hadoop default configuration for simplicity 57 | File file = new File("app/hadoop-3.3.0/share/doc/hadoop/hadoop-project-dist/hadoop-common/core-default.xml"); 58 | Document doc = builder.parse(file); 59 | Set defaultConfig = new HashSet<>(); 60 | NodeList nodes = doc.getElementsByTagName("configuration"); 61 | NodeList childList = nodes.item(0).getChildNodes(); 62 | for (int i = 0; i < childList.getLength(); i++) { 63 | Node propertyNode = childList.item(i); 64 | NodeList propertyNodeList = propertyNode.getChildNodes(); 65 | String nodeName = ""; 66 | for (int j = 0; j < propertyNodeList.getLength(); j++) { 67 | Node node= propertyNodeList.item(j); 68 | if (node.getNodeName() == "name") { 69 | nodeName = node.getTextContent(); 70 | } else if (node.getNodeName() == "value") { 71 | // if value tag is empty, we don't consider it as unused parameter 72 | if (node.getTextContent() != "") { 73 | defaultConfig.add(nodeName); 74 | } 75 | break; 76 | } 77 | } 78 | } 79 | 80 | return defaultConfig; 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /archive/config_files/zookeeper-default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | clientPort 5 | (none) 6 | the port to listen for client connections; that is, the 7 | port that clients attempt to connect to. 8 | 9 | 10 | 11 | secureClientPort 12 | (none) 13 | the port to listen on for secure client connections using SSL. 14 | 15 | clientPort specifies 16 | the port for plaintext connections while 17 | secureClientPort specifies the port for SSL 18 | connections. Specifying both enables mixed-mode while omitting 19 | either will disable that mode. 20 | Note that SSL feature will be enabled when user plugs-in 21 | zookeeper.serverCnxnFactory, zookeeper.clientCnxnSocket as Netty. 22 | 23 | 24 | 25 | dataDir 26 | (none) 27 | the location where ZooKeeper will store the in-memory 28 | database snapshots and, unless specified otherwise, the 29 | transaction log of updates to the database. 30 | Be careful where you put the transaction log. A 31 | dedicated transaction log device is key to consistent good 32 | performance. Putting the log on a busy device will adversely 33 | effect performance. 34 | 35 | 36 | 37 | tickTime 38 | (none) 39 | the length of a single tick, which is the basic time unit 40 | used by ZooKeeper, as measured in milliseconds. It is used to 41 | regulate heartbeats, and timeouts. For example, the minimum 42 | session timeout will be two ticks. 43 | 44 | 45 | 46 | dataLogDir 47 | (none) 48 | (No Java system property) 49 | This option will direct the machine to write the 50 | transaction log to the dataLogDir rather than the dataDir. This allows a dedicated log 51 | device to be used, and helps avoid competition between logging 52 | and snaphots. 53 | Having a dedicated log device has a large impact on 54 | throughput and stable latencies. It is highly recommened to 55 | dedicate a log device and set dataLogDir to point to a directory on 56 | that device, and then make sure to point dataDir to a directory 57 | not residing on that device. 58 | 59 | 60 | 61 | globalOutstandingLimit 62 | (none) 63 | (Java system property: zookeeper.globalOutstandingLimit.) 64 | Clients can submit requests faster than ZooKeeper can 65 | process them, especially if there are a lot of clients. To 66 | prevent ZooKeeper from running out of memory due to queued 67 | requests, ZooKeeper will throttle clients so that there is no 68 | more than globalOutstandingLimit outstanding requests in the 69 | system. The default limit is 1,000. 70 | 71 | 72 | 73 | preAllocSize 74 | (none) 75 | (Java system property: zookeeper.preAllocSize) 76 | To avoid seeks ZooKeeper allocates space in the 77 | transaction log file in blocks of preAllocSize kilobytes. The 78 | default block size is 64M. One reason for changing the size of 79 | the blocks is to reduce the block size if snapshots are taken 80 | more often. (Also, see snapCount). 81 | 82 | 83 | 84 | snapCount 85 | (none) 86 | (Java system property: zookeeper.snapCount) 87 | ZooKeeper records its transactions using snapshots and 88 | a transaction log (think write-ahead log).The number of 89 | transactions recorded in the transaction log before a snapshot 90 | can be taken (and the transaction log rolled) is determined 91 | by snapCount. In order to prevent all of the machines in the quorum 92 | from taking a snapshot at the same time, each ZooKeeper server 93 | will take a snapshot when the number of transactions in the transaction log 94 | reaches a runtime generated random value in the [snapCount/2+1, snapCount] 95 | range.The default snapCount is 100,000. 96 | 97 | 98 | 99 | maxClientCnxns 100 | (none) 101 | (No Java system property) 102 | Limits the number of concurrent connections (at the socket 103 | level) that a single client, identified by IP address, may make 104 | to a single member of the ZooKeeper ensemble. This is used to 105 | prevent certain classes of DoS attacks, including file 106 | descriptor exhaustion. The default is 60. Setting this to 0 107 | entirely removes the limit on concurrent connections. 108 | 109 | 110 | 111 | clientPortAddress 112 | (none) 113 | 114 | New in 3.3.0: the 115 | address (ipv4, ipv6 or hostname) to listen for client 116 | connections; that is, the address that clients attempt 117 | to connect to. This is optional, by default we bind in 118 | such a way that any connection to the clientPort for any 119 | address/interface/nic on the server will be 120 | accepted. 121 | 122 | 123 | 124 | minSessionTimeout 125 | (none) 126 | (No Java system property) 127 | 128 | New in 3.3.0: the 129 | minimum session timeout in milliseconds that the server 130 | will allow the client to negotiate. Defaults to 2 times 131 | the tickTime. 132 | 133 | 134 | 135 | maxSessionTimeout 136 | (none) 137 | (No Java system property) 138 | 139 | New in 3.3.0: the 140 | maximum session timeout in milliseconds that the server 141 | will allow the client to negotiate. Defaults to 20 times 142 | the tickTime. 143 | 144 | 145 | 146 | fsync.warningthresholdms 147 | (none) 148 | (Java system property: zookeeper.fsync.warningthresholdms) 149 | 150 | New in 3.3.4: A 151 | warning message will be output to the log whenever an 152 | fsync in the Transactional Log (WAL) takes longer than 153 | this value. The values is specified in milliseconds and 154 | defaults to 1000. This value can only be set as a 155 | system property. 156 | 157 | 158 | 159 | autopurge.snapRetainCount 160 | (none) 161 | (No Java system property) 162 | 163 | New in 3.4.0: 164 | When enabled, ZooKeeper auto purge feature retains 165 | the autopurge.snapRetainCount most 166 | recent snapshots and the corresponding transaction logs in the 167 | dataDir and dataLogDir respectively and deletes the rest. 168 | Defaults to 3. Minimum value is 3. 169 | 170 | 171 | 172 | autopurge.purgeInterval 173 | (none) 174 | (No Java system property) 175 | 176 | New in 3.4.0: The 177 | time interval in hours for which the purge task has to 178 | be triggered. Set to a positive integer (1 and above) 179 | to enable the auto purging. Defaults to 0. 180 | 181 | 182 | 183 | syncEnabled 184 | (none) 185 | (Java system property: zookeeper.observer.syncEnabled) 186 | 187 | New in 3.4.6, 3.5.0: 188 | The observers now log transaction and write snapshot to disk 189 | by default like the participants. This reduces the recovery time 190 | of the observers on restart. Set to "false" to disable this 191 | feature. Default is "true" 192 | 193 | 194 | 195 | zookeeper.extendedTypesEnabled 196 | (none) 197 | (Java system property only: zookeeper.extendedTypesEnabled) 198 | 199 | New in 3.5.4, 3.6.0: Define to "true" to enable 200 | extended features such as the creation of TTL Nodes. 201 | They are disabled by default. IMPORTANT: when enabled server IDs must 202 | be less than 255 due to internal limitations. 203 | 204 | 205 | 206 | 207 | zookeeper.emulate353TTLNodes 208 | (none) 209 | (Java system property only: zookeeper.emulate353TTLNodes) 210 | 211 | New in 3.5.4, 3.6.0: Due to 212 | ZOOKEEPER-2901 TTL nodes 213 | created in version 3.5.3 are not supported in 3.5.4/3.6.0. However, a workaround is provided via the 214 | zookeeper.emulate353TTLNodes system property. If you used TTL nodes in ZooKeeper 3.5.3 and need to maintain 215 | compatibility set zookeeper.emulate353TTLNodes to "true" in addition to 216 | zookeeper.extendedTypesEnabled. NOTE: due to the bug, server IDs 217 | must be 127 or less. Additionally, the maximum support TTL value is 1099511627775 which is smaller 218 | than what was allowed in 3.5.3 (1152921504606846975) 219 | 220 | 221 | 222 | electionAlg 223 | (none) 224 | (No Java system property) 225 | Election implementation to use. A value of "0" corresponds 226 | to the original UDP-based version, "1" corresponds to the 227 | non-authenticated UDP-based version of fast leader election, "2" 228 | corresponds to the authenticated UDP-based version of fast 229 | leader election, and "3" corresponds to TCP-based version of 230 | fast leader election. Currently, algorithm 3 is the default 231 | The implementations of leader election 0, 1, and 2 are now 232 | deprecated . We have the intention 233 | of removing them in the next release, at which point only the 234 | FastLeaderElection will be available. 235 | 236 | 237 | 238 | 239 | initLimit 240 | (none) 241 | (No Java system property) 242 | Amount of time, in ticks (see tickTime), to allow followers to 243 | connect and sync to a leader. Increased this value as needed, if 244 | the amount of data managed by ZooKeeper is large. 245 | 246 | 247 | 248 | leaderServes 249 | (none) 250 | (Java system property: zookeeper.leaderServes) 251 | Leader accepts client connections. Default value is "yes". 252 | The leader machine coordinates updates. For higher update 253 | throughput at thes slight expense of read throughput the leader 254 | can be configured to not accept clients and focus on 255 | coordination. The default to this option is yes, which means 256 | that a leader will accept client connections. 257 | Turning on leader selection is highly recommended when 258 | you have more than three ZooKeeper servers in an ensemble. 259 | 260 | 261 | 262 | server.x=[hostname]nnnnn[nnnnn], etc 263 | (none) 264 | (No Java system property) 265 | servers making up the ZooKeeper ensemble. When the server 266 | starts up, it determines which server it is by looking for the 267 | file myid in the data directory. That file 268 | contains the server number, in ASCII, and it should match 269 | x in server.x in the left hand side of this 270 | setting. 271 | The list of servers that make up ZooKeeper servers that is 272 | used by the clients must match the list of ZooKeeper servers 273 | that each ZooKeeper server has. 274 | There are two port numbers nnnnn. 275 | The first followers use to connect to the leader, and the second is for 276 | leader election. The leader election port is only necessary if electionAlg 277 | is 1, 2, or 3 (default). If electionAlg is 0, then the second port is not 278 | necessary. If you want to test multiple servers on a single machine, then 279 | different ports can be used for each server. 280 | 281 | 282 | 283 | syncLimit 284 | (none) 285 | (No Java system property) 286 | Amount of time, in ticks (see tickTime), to allow followers to sync 287 | with ZooKeeper. If followers fall too far behind a leader, they 288 | will be dropped. 289 | 290 | 291 | 292 | group.x=nnnnn[nnnnn] 293 | (none) 294 | (No Java system property) 295 | Enables a hierarchical quorum construction."x" is a group identifier 296 | and the numbers following the "=" sign correspond to server identifiers. 297 | The left-hand side of the assignment is a colon-separated list of server 298 | identifiers. Note that groups must be disjoint and the union of all groups 299 | must be the ZooKeeper ensemble. 300 | You will find an example here 301 | 302 | 303 | 304 | 305 | 306 | weight.x=nnnnn 307 | (none) 308 | (No Java system property) 309 | Used along with "group", it assigns a weight to a server when 310 | forming quorums. Such a value corresponds to the weight of a server 311 | when voting. There are a few parts of ZooKeeper that require voting 312 | such as leader election and the atomic broadcast protocol. By default 313 | the weight of server is 1. If the configuration defines groups, but not 314 | weights, then a value of 1 will be assigned to all servers. 315 | 316 | You will find an example here 317 | 318 | 319 | 320 | 321 | 322 | cnxTimeout 323 | (none) 324 | (Java system property: zookeeper.cnxTimeout) 325 | Sets the timeout value for opening connections for leader election notifications. 326 | Only applicable if you are using electionAlg 3. 327 | 328 | Default value is 5 seconds. 329 | 330 | 331 | 332 | standaloneEnabled 333 | (none) 334 | (No Java system property) 335 | 336 | New in 3.5.0: 337 | When set to false, a single server can be started in replicated 338 | mode, a lone participant can run with observers, and a cluster 339 | can reconfigure down to one node, and up from one node. The 340 | default is true for backwards compatibility. It can be set 341 | using QuorumPeerConfig's setStandaloneEnabled method or by 342 | adding "standaloneEnabled=false" or "standaloneEnabled=true" 343 | to a server's config file. 344 | 345 | 346 | 347 | 348 | reconfigEnabled 349 | (none) 350 | (No Java system property) 351 | 352 | New in 3.5.3: 353 | This controls the enabling or disabling of 354 | 355 | Dynamic Reconfiguration feature. When the feature 356 | is enabled, users can perform reconfigure operations through 357 | the ZooKeeper client API or through ZooKeeper command line tools 358 | assuming users are authorized to perform such operations. 359 | When the feature is disabled, no user, including the super user, 360 | can perform a reconfiguration. Any attempt to reconfigure will return an error. 361 | "reconfigEnabled" option can be set as 362 | "reconfigEnabled=false" or 363 | "reconfigEnabled=true" 364 | to a server's config file, or using QuorumPeerConfig's 365 | setReconfigEnabled method. The default value is false. 366 | 367 | If present, the value should be consistent across every server in 368 | the entire ensemble. Setting the value as true on some servers and false 369 | on other servers will cause inconsistent behavior depending on which server 370 | is elected as leader. If the leader has a setting of 371 | "reconfigEnabled=true", then the ensemble 372 | will have reconfig feature enabled. If the leader has a setting of 373 | "reconfigEnabled=false", then the ensemble 374 | will have reconfig feature disabled. It is thus recommended to have a consistent 375 | value for "reconfigEnabled" across servers 376 | in the ensemble. 377 | 378 | 379 | 380 | 381 | 4lw.commands.whitelist 382 | (none) 383 | (Java system property: zookeeper.4lw.commands.whitelist) 384 | 385 | New in 3.5.3: 386 | A list of comma separated Four Letter Words 387 | commands that user wants to use. A valid Four Letter Words 388 | command must be put in this list else ZooKeeper server will 389 | not enable the command. 390 | By default the whitelist only contains "srvr" command 391 | which zkServer.sh uses. The rest of four letter word commands are disabled 392 | by default. 393 | 394 | Here's an example of the configuration that enables stat, ruok, conf, and isro 395 | command while disabling the rest of Four Letter Words command: 396 | If you really need enable all four letter word commands by default, you can use 397 | the asterisk option so you don't have to include every command one by one in the list. 398 | As an example, this will enable all four letter word commands: 399 | 400 | 401 | 402 | 403 | tcpKeepAlive 404 | (none) 405 | (Java system property: zookeeper.tcpKeepAlive) 406 | 407 | New in 3.5.4: 408 | Setting this to true sets the TCP keepAlive flag on the 409 | sockets used by quorum members to perform elections. 410 | This will allow for connections between quorum members to 411 | remain up when there is network infrastructure that may 412 | otherwise break them. Some NATs and firewalls may terminate 413 | or lose state for long running or idle connections. 414 | Enabling this option relies on OS level settings to work 415 | properly, check your operating system's options regarding TCP 416 | keepalive for more information. Defaults to 417 | false. 418 | 419 | 420 | 421 | 422 | DigestAuthenticationProvider.superDigest 423 | (none) 424 | (Java system property: zookeeper.DigestAuthenticationProvider.superDigest) 425 | By default this feature is disabled 426 | 427 | 428 | New in 3.2: 429 | Enables a ZooKeeper ensemble administrator to access the 430 | znode hierarchy as a "super" user. In particular no ACL 431 | checking occurs for a user authenticated as 432 | super. 433 | org.apache.zookeeper.server.auth.DigestAuthenticationProvider 434 | can be used to generate the superDigest, call it with 435 | one parameter of "super:<password>". Provide the 436 | generated "super:<data>" as the system property value 437 | when starting each server of the ensemble. 438 | When authenticating to a ZooKeeper server (from a 439 | ZooKeeper client) pass a scheme of "digest" and authdata 440 | of "super:<password>". Note that digest auth passes 441 | the authdata in plaintext to the server, it would be 442 | prudent to use this authentication method only on 443 | localhost (not over the network) or over an encrypted 444 | connection. 445 | 446 | 447 | 448 | X509AuthenticationProvider.superUser 449 | (none) 450 | (Java system property: zookeeper.X509AuthenticationProvider.superUser) 451 | The SSL-backed way to enable a ZooKeeper ensemble 452 | administrator to access the znode hierarchy as a "super" user. 453 | When this parameter is set to an X500 principal name, only an 454 | authenticated client with that principal will be able to bypass 455 | ACL checking and have full privileges to all znodes. 456 | 457 | 458 | 459 | zookeeper.superUser 460 | (none) 461 | (Java system property: zookeeper.superUser) 462 | Similar to zookeeper.X509AuthenticationProvider.superUser 463 | but is generic for SASL based logins. It stores the name of 464 | a user that can access the znode hierarchy as a "super" user. 465 | 466 | 467 | 468 | 469 | ssl.keyStore.location and ssl.keyStore.password 470 | (none) 471 | (Java system properties: 472 | zookeeper.ssl.keyStore.location and zookeeper.ssl.keyStore.password) 473 | Specifies the file path to a JKS containing the local 474 | credentials to be used for SSL connections, and the 475 | password to unlock the file. 476 | 477 | 478 | 479 | ssl.trustStore.location and ssl.trustStore.password 480 | (none) 481 | (Java system properties: 482 | zookeeper.ssl.trustStore.location and zookeeper.ssl.trustStore.password) 483 | Specifies the file path to a JKS containing the remote 484 | credentials to be used for SSL connections, and the 485 | password to unlock the file. 486 | 487 | 488 | 489 | ssl.authProvider 490 | (none) 491 | (Java system property: zookeeper.ssl.authProvider) 492 | Specifies a subclass of 493 | org.apache.zookeeper.auth.X509AuthenticationProvider 494 | to use for secure client authentication. This is useful in 495 | certificate key infrastructures that do not use JKS. It may be 496 | necessary to extend javax.net.ssl.X509KeyManager 497 | and javax.net.ssl.X509TrustManager 498 | to get the desired behavior from the SSL stack. To configure the 499 | ZooKeeper server to use the custom provider for authentication, 500 | choose a scheme name for the custom AuthenticationProvider and 501 | set the property zookeeper.authProvider.[scheme] 502 | to the fully-qualified class name of the custom 503 | implementation. This will load the provider into the ProviderRegistry. 504 | Then set this property 505 | zookeeper.ssl.authProvider=[scheme] and that provider 506 | will be used for secure authentication. 507 | 508 | 509 | 510 | Read Only Mode Server 511 | (none) 512 | (Java system property: readonlymode.enabled) 513 | 514 | New in 3.4.0: 515 | Setting this value to true enables Read Only Mode server 516 | support (disabled by default). ROM allows clients 517 | sessions which requested ROM support to connect to the 518 | server even when the server might be partitioned from 519 | the quorum. In this mode ROM clients can still read 520 | values from the ZK service, but will be unable to write 521 | values and see changes from other clients. See 522 | ZOOKEEPER-784 for more details. 523 | 524 | 525 | 526 | 527 | forceSync 528 | (none) 529 | (Java system property: zookeeper.forceSync) 530 | Requires updates to be synced to media of the transaction 531 | log before finishing processing the update. If this option is 532 | set to no, ZooKeeper will not require updates to be synced to 533 | the media. 534 | 535 | 536 | 537 | jute.maxbuffer 538 | (none) 539 | (Java system property: 540 | jute.maxbuffer) 541 | This option can only be set as a Java system property. 542 | There is no zookeeper prefix on it. It specifies the maximum 543 | size of the data that can be stored in a znode. The default is 544 | 0xfffff, or just under 1M. If this option is changed, the system 545 | property must be set on all servers and clients otherwise 546 | problems will arise. This is really a sanity check. ZooKeeper is 547 | designed to store data on the order of kilobytes in size. 548 | 549 | 550 | 551 | skipACL 552 | (none) 553 | (Java system property: zookeeper.skipACL) 554 | Skips ACL checks. This results in a boost in throughput, 555 | but opens up full access to the data tree to everyone. 556 | 557 | 558 | 559 | quorumListenOnAllIPs 560 | (none) 561 | When set to true the ZooKeeper server will listen 562 | for connections from its peers on all available IP addresses, 563 | and not only the address configured in the server list of the 564 | configuration file. It affects the connections handling the 565 | ZAB protocol and the Fast Leader Election protocol. Default 566 | value is false. 567 | 568 | 569 | 570 | zookeeper.nio.numSelectorThreads 571 | (none) 572 | (Java system property only: zookeeper.nio.numSelectorThreads) 573 | 574 | 575 | New in 3.5.0: 576 | Number of NIO selector threads. At least 1 selector thread required. 577 | It is recommended to use more than one selector for large numbers 578 | of client connections. The default value is sqrt( number of cpu cores / 2 ). 579 | 580 | 581 | 582 | 583 | zookeeper.nio.numWorkerThreads 584 | (none) 585 | (Java system property only: zookeeper.nio.numWorkerThreads) 586 | 587 | 588 | New in 3.5.0: 589 | Number of NIO worker threads. If configured with 0 worker threads, the selector threads 590 | do the socket I/O directly. The default value is 2 times the number of cpu cores. 591 | 592 | 593 | 594 | 595 | zookeeper.commitProcessor.numWorkerThreads 596 | (none) 597 | (Java system property only: zookeeper.commitProcessor.numWorkerThreads) 598 | 599 | 600 | New in 3.5.0: 601 | Number of Commit Processor worker threads. If configured with 0 worker threads, the main thread 602 | will process the request directly. The default value is the number of cpu cores. 603 | 604 | 605 | 606 | 607 | znode.container.checkIntervalMs 608 | (none) 609 | (Java system property only) 610 | 611 | New in 3.5.1: The 612 | time interval in milliseconds for each check of candidate container 613 | and ttl nodes. Default is "60000". 614 | 615 | 616 | 617 | znode.container.maxPerMinute 618 | (none) 619 | (Java system property only) 620 | 621 | New in 3.5.1: The 622 | maximum number of container nodes that can be deleted per 623 | minute. This prevents herding during container deletion. 624 | Default is "10000". 625 | 626 | 627 | 628 | admin.enableServer 629 | (none) 630 | (Java system property: zookeeper.admin.enableServer) 631 | Set to "false" to disable the AdminServer. By default the 632 | AdminServer is enabled. 633 | 634 | 635 | 636 | admin.serverAddress 637 | (none) 638 | (Java system property: zookeeper.admin.serverAddress) 639 | The address the embedded Jetty server listens on. Defaults to 0.0.0.0. 640 | 641 | 642 | 643 | admin.serverPort 644 | (none) 645 | (Java system property: zookeeper.admin.serverPort) 646 | The port the embedded Jetty server listens on. Defaults to 8080. 647 | 648 | 649 | 650 | admin.idleTimeout 651 | (none) 652 | (Java system property: zookeeper.admin.idleTimeout) 653 | Set the maximum idle time in milliseconds that a connection can wait 654 | before sending or receiving data. Defaults to 30000 ms. 655 | 656 | 657 | 658 | admin.commandURL 659 | (none) 660 | (Java system property: zookeeper.admin.commandURL) 661 | The URL for listing and issuing commands relative to the 662 | root URL. Defaults to "/commands". 663 | 664 | 665 | 666 | -------------------------------------------------------------------------------- /archive/notes/caseSensitivityCheck.md: -------------------------------------------------------------------------------- 1 | # Case sensitivity check 2 | 3 | ## Methodology 4 | 5 | 1. Only string type parameters are considered. 6 | 2. `equals` and `equalsIgnoreCase` should not both appear in the usages of the same parameter. 7 | 3. The arguments of `equals` for all usages of the same parameter should be all uppercase or all lowercase or all mixture. Otherwise the following inconsistency may happen: 8 | 9 | ![img](caseSensitivityCheckPic/1.png) 10 | 11 | ## Current problem 12 | 13 | 1. The value of arguments may not be directly presented as strings. Sometimes the arguments are tokens in soot such as r2, $r3. We cannot figure out the concrete value and analyze its case type. 14 | 15 | ![](caseSensitivityCheckPic/4.png) 16 | 17 | 18 | 19 | After discussion in meeting, we assume that for some cases, the value of token may be able to be achieved. For example, a token $r1 is here in path[2]: 20 | 21 | ![](caseSensitivityCheckPic/2.png) 22 | 23 | 24 | 25 | The corresponding code in project is 26 | 27 | ``` 28 | "murmur".equalsIgnoreCase(name) 29 | ``` 30 | 31 | So we currently think that if we force to change the type of token or try to do some hacks, it is possible to get the value. 32 | 33 | 34 | 35 | However, under most cases the value cannot be determined that simply. It may not be deterministic so it is impossible to get the concrete value. 36 | 37 | 38 | 39 | 2. Some usages of `equals` or `equalsIgnoreCase` are not related to the parameter, but they are present in the flow and taken into consideration. Such usages may lead to unnecessary inconsistency warnings. 40 | 41 | Here is an example. In a flow whose source is `get("hadoop.ssl.enabled.protocol")`, an `equalsIgnoreCase` appears in the flow after a very very long process. 42 | 43 | ![](caseSensitivityCheckPic/5.png) 44 | 45 | The corresponding code in project is 46 | 47 | ``` 48 | if ("https".equalsIgnoreCase(kmsUrl.getProtocol())) 49 | ``` 50 | 51 | So here the method `equalsIgnoreCase()` is used to compare the value with "http". However, the default value of parameter `"hadoop.ssl.enabled.protocol"` is `"TLSv1,SSLv2Hello,TLSv1.1,TLSv1.2"`, which is obviously irrelevant with "http" . Therefore, the usage of `equalsIgnoreCase()` here has nothing to do with case sensitivity of the parameter. But it is present in the flow, and I currently cannot think out a way to distinguish it from those `equalsIgnoreCase()` which are actually related to the parameter. 52 | 53 | 54 | 55 | 3. The result changes when I run the program, which may undermine the correctness of the analysis. In other words, a flow is possible to be present in the outcome, and it is also possible to be not there. 56 | 57 | 2 versions of different logs: 58 | 59 | ``` 60 | "hadoop.ssl.client.conf" : 61 | "final"(equals) 62 | 63 | "bind.address" : 64 | "0.0.0.0"(equals) 65 | 66 | "hadoop.ssl.hostname.verifier" : 67 | "STRICT_IE6"(equals) 68 | "ALLOW_ALL"(equals) 69 | "STRICT"(equals) 70 | "DEFAULT"(equals) 71 | "DEFAULT_AND_LOCALHOST"(equals) 72 | 73 | "zk-dt-secret-manager.zkAuthType" : 74 | "none"(equals) 75 | "sasl"(equals) 76 | "sasl"(equals) 77 | 78 | "hadoop.security.authentication" : 79 | $r2(equals) 80 | $r2(equals) 81 | $r2(equals) 82 | 83 | "fs.ftp.data.connection.mode" : 84 | "ACTIVE_LOCAL_DATA_CONNECTION_MODE"(equals) 85 | "PASSIVE_LOCAL_DATA_CONNECTION_MODE"(equals) 86 | "PASSIVE_REMOTE_DATA_CONNECTION_MODE"(equals) 87 | 88 | "zk-dt-secret-manager.kerberos.principal" : 89 | "_HOST"(equals) 90 | 91 | "hadoop.kms.http.host" : 92 | "0.0.0.0"(equals) 93 | 94 | "hadoop.security.dns.nameserver" : 95 | "0.0.0.0"(equals) 96 | 97 | "fs.defaultFS" : 98 | $r14(equals) 99 | "file:///"(equals) 100 | 101 | "hadoop.kms.http.administrators" : 102 | "*"(equals) 103 | 104 | "nfs.exports.allowed.hosts" : 105 | "*"(equals) 106 | 107 | "hadoop.util.hash.type" : 108 | r0(equalsIgnoreCase) 109 | r0(equalsIgnoreCase) 110 | 111 | "hadoop.ssl.server.conf" : 112 | "final"(equals) 113 | 114 | "hadoop.security.credential.provider.path" : 115 | ""(equals) 116 | 117 | "io.compression.codec.bzip2.library" : 118 | "java-builtin"(equals) 119 | 120 | "hadoop.security.dns.interface" : 121 | "0.0.0.0"(equals) 122 | 123 | "fs.ftp.transfer.mode" : 124 | "STREAM_TRANSFER_MODE"(equals) 125 | "BLOCK_TRANSFER_MODE"(equals) 126 | "COMPRESSED_TRANSFER_MODE"(equals) 127 | 128 | "tmpjars" : 129 | $r14(equals) 130 | ``` 131 | 132 | and 133 | 134 | ``` 135 | "hadoop.ssl.client.conf" : 136 | "fallback"(used in method "equals") 137 | "property"(used in method "equals") 138 | "value"(used in method "equals") 139 | 140 | "bind.address" : 141 | "0.0.0.0"(used in method "equals") 142 | 143 | "hadoop.ssl.hostname.verifier" : 144 | "STRICT"(used in method "equals") 145 | "DEFAULT_AND_LOCALHOST"(used in method "equals") 146 | "STRICT_IE6"(used in method "equals") 147 | "ALLOW_ALL"(used in method "equals") 148 | "DEFAULT"(used in method "equals") 149 | 150 | "zk-dt-secret-manager.zkAuthType" : 151 | "sasl"(used in method "equals") 152 | "none"(used in method "equals") 153 | "sasl"(used in method "equals") 154 | 155 | "fs.ftp.data.connection.mode" : 156 | "ACTIVE_LOCAL_DATA_CONNECTION_MODE"(used in method "equals") 157 | "PASSIVE_REMOTE_DATA_CONNECTION_MODE"(used in method "equals") 158 | "PASSIVE_LOCAL_DATA_CONNECTION_MODE"(used in method "equals") 159 | 160 | "hadoop.security.authentication" : 161 | $r2(used in method "equals") 162 | $r2(used in method "equals") 163 | $r2(used in method "equals") 164 | 165 | "hadoop.kms.http.host" : 166 | "0.0.0.0"(used in method "equals") 167 | 168 | "zk-dt-secret-manager.kerberos.principal" : 169 | "_HOST"(used in method "equals") 170 | 171 | "fs.defaultFS" : 172 | "true"(used in method "equalsIgnoreCase") 173 | r2(used in method "equalsIgnoreCase") 174 | "file:///"(used in method "equals") 175 | 176 | "hadoop.kms.http.administrators" : 177 | "*"(used in method "equals") 178 | 179 | "hadoop.util.hash.type" : 180 | r0(used in method "equalsIgnoreCase") 181 | r0(used in method "equalsIgnoreCase") 182 | 183 | "hadoop.ssl.server.conf" : 184 | "fallback"(used in method "equals") 185 | "property"(used in method "equals") 186 | "value"(used in method "equals") 187 | 188 | "hadoop.security.credential.provider.path" : 189 | r1(used in method "equals") 190 | 191 | "io.compression.codec.bzip2.library" : 192 | "java-builtin"(used in method "equals") 193 | 194 | "hadoop.security.key.default.cipher" : 195 | "true"(used in method "equalsIgnoreCase") 196 | r2(used in method "equalsIgnoreCase") 197 | "fallback"(used in method "equals") 198 | "property"(used in method "equals") 199 | "value"(used in method "equals") 200 | 201 | "fs.ftp.transfer.mode" : 202 | "BLOCK_TRANSFER_MODE"(used in method "equals") 203 | "COMPRESSED_TRANSFER_MODE"(used in method "equals") 204 | "STREAM_TRANSFER_MODE"(used in method "equals") 205 | 206 | "tmpjars" : 207 | "true"(used in method "equalsIgnoreCase") 208 | r2(used in method "equalsIgnoreCase") 209 | ``` 210 | 211 | 212 | 213 | Some flows I encountered differ from the flows recorded in `/flows` of `ccc` repository as well. 214 | 215 | Personally, I suspect the phenomenon is due to the balance between correctness and accuracy in soot, and may be related to the set of soot configuration. But it needs more actions and tries to be proved. 216 | -------------------------------------------------------------------------------- /archive/notes/caseSensitivityCheckPic/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlab-uiuc/cflow/30fb3933b52f8ea837edce2415bf9ef84d25f0ab/archive/notes/caseSensitivityCheckPic/1.png -------------------------------------------------------------------------------- /archive/notes/caseSensitivityCheckPic/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlab-uiuc/cflow/30fb3933b52f8ea837edce2415bf9ef84d25f0ab/archive/notes/caseSensitivityCheckPic/2.png -------------------------------------------------------------------------------- /archive/notes/caseSensitivityCheckPic/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlab-uiuc/cflow/30fb3933b52f8ea837edce2415bf9ef84d25f0ab/archive/notes/caseSensitivityCheckPic/3.png -------------------------------------------------------------------------------- /archive/notes/caseSensitivityCheckPic/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlab-uiuc/cflow/30fb3933b52f8ea837edce2415bf9ef84d25f0ab/archive/notes/caseSensitivityCheckPic/4.png -------------------------------------------------------------------------------- /archive/notes/caseSensitivityCheckPic/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlab-uiuc/cflow/30fb3933b52f8ea837edce2415bf9ef84d25f0ab/archive/notes/caseSensitivityCheckPic/5.png -------------------------------------------------------------------------------- /archive/notes/caseSensitivityCheckPic/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlab-uiuc/cflow/30fb3933b52f8ea837edce2415bf9ef84d25f0ab/archive/notes/caseSensitivityCheckPic/6.png -------------------------------------------------------------------------------- /doc/cflow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlab-uiuc/cflow/30fb3933b52f8ea837edce2415bf9ef84d25f0ab/doc/cflow.jpg -------------------------------------------------------------------------------- /doc/cflow_report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlab-uiuc/cflow/30fb3933b52f8ea837edce2415bf9ef84d25f0ab/doc/cflow_report.pdf -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | ccc 8 | ccc 9 | 1.0 10 | 11 | 12 | UTF-8 13 | UTF-8 14 | 15 | 16 | 17 | 1.8 18 | ${maven.compiler.source} 19 | 20 | 5.2.0 21 | 1.2.0 22 | 0.8.1 23 | 24 | 25 | 26 | 27 | 28 | org.apache.maven.plugins 29 | maven-compiler-plugin 30 | 3.8.1 31 | 32 | 1.8 33 | 1.8 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | org.soot-oss 42 | soot 43 | 4.2.1 44 | 45 | 46 | org.slf4j 47 | slf4j-simple 48 | 1.7.30 49 | 50 | 51 | commons-cli 52 | commons-cli 53 | 1.4 54 | 55 | 56 | 57 | 58 | org.junit.jupiter 59 | junit-jupiter-api 60 | ${junit.jupiter.version} 61 | test 62 | 63 | 64 | org.junit.jupiter 65 | junit-jupiter-params 66 | ${junit.jupiter.version} 67 | test 68 | 69 | 70 | org.junit.jupiter 71 | junit-jupiter-engine 72 | ${junit.jupiter.version} 73 | test 74 | 75 | 76 | junit 77 | junit 78 | 4.13.1 79 | test 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | intra="" 4 | spark="" 5 | while getopts ":a::i::s" opt; do 6 | case ${opt} in 7 | a) 8 | target=$OPTARG 9 | ;; 10 | i) 11 | intra="-intra" 12 | ;; 13 | s) 14 | spark="-spark" 15 | ;; 16 | *) 17 | echo "Usage: run.sh -a x (x is any or a combination of the following options separated by ',')" 18 | echo " hdfs" 19 | echo " mapreduce" 20 | echo " yarn" 21 | echo " hadoop_common" 22 | echo " hadoop_tools" 23 | echo " hbase" 24 | echo " alluxio" 25 | echo " zookeeper" 26 | echo " spark" 27 | exit 1 28 | ;; 29 | esac 30 | done 31 | 32 | rm tmp.txt 33 | export MAVEN_OPTS=-Xmx6g 34 | mvn exec:java -Dexec.mainClass="Main" -Dexec.args="-o tmp.txt -a ${target} ${intra} ${spark}" -e 35 | -------------------------------------------------------------------------------- /src/main/java/Main.java: -------------------------------------------------------------------------------- 1 | import configInterface.ConfigInterface; 2 | import org.apache.commons.cli.*; 3 | import taintAnalysis.TaintAnalysisDriver; 4 | import taintAnalysis.sourceSinkManager.ISourceSinkManager; 5 | import taintAnalysis.sourceSinkManager.SourceSinkManager; 6 | import taintAnalysis.taintWrapper.ITaintWrapper; 7 | import taintAnalysis.taintWrapper.TaintWrapper; 8 | import utility.Config; 9 | 10 | import java.io.FileOutputStream; 11 | import java.io.IOException; 12 | import java.io.PrintStream; 13 | import java.util.*; 14 | 15 | public class Main { 16 | 17 | public static void main(String[] args) throws IOException { 18 | Option optionApp = Option.builder("a") 19 | .required(true) 20 | .desc("Support applications are: test, hdfs, mapreduce, yarn, hadoop_common, hadoop_tools, hbase, alluxio, zookeeper, spark") 21 | .longOpt("app") 22 | .hasArg() 23 | .build(); 24 | 25 | Option optionOutput = Option.builder("o") 26 | .required(false) 27 | .desc("This parameter specifies the exported file path. If not specified, output will be directed to stdout.") 28 | .longOpt("output") 29 | .hasArg() 30 | .build(); 31 | 32 | Option optionSpark = Option.builder(null) 33 | .required(false) 34 | .desc("Use Soot's SPARK for call graph (more precise but expensive)") 35 | .longOpt("spark") 36 | .hasArg(false) 37 | .build(); 38 | 39 | Option optionIntra = Option.builder(null) 40 | .required(false) 41 | .desc("Run intra-procedural analysis (testing only)") 42 | .longOpt("intra") 43 | .hasArg(false) 44 | .build(); 45 | 46 | Options options = new Options(); 47 | options.addOption(optionApp); 48 | options.addOption(optionOutput); 49 | options.addOption(optionSpark); 50 | options.addOption(optionIntra); 51 | 52 | CommandLineParser parser = new DefaultParser(); 53 | try { 54 | CommandLine commandLine = parser.parse(options, args); 55 | boolean use_spark = false; 56 | boolean run_intra = false; 57 | 58 | /* getting required parameters */ 59 | /* getting option a */ 60 | String apps = commandLine.getOptionValue('a'); 61 | String[] result = apps.split(","); 62 | String[][] considered = new String[result.length][]; 63 | for (int i = 0; i < considered.length; i++) { 64 | try { 65 | considered[i] = Config.getCfg(result[i]); 66 | } catch (IllegalArgumentException e) { 67 | throw new ParseException(result[i] + " not found in supported application"); 68 | } 69 | } 70 | 71 | /* getting optional parameters */ 72 | if (commandLine.hasOption('o')) { 73 | /* getting option o */ 74 | String filePath = commandLine.getOptionValue('o'); 75 | PrintStream fileOut = new PrintStream(new FileOutputStream(filePath, true)); 76 | System.setOut(fileOut); 77 | } 78 | if (commandLine.hasOption("spark")) { 79 | /* getting option spark */ 80 | use_spark = true; 81 | } 82 | if (commandLine.hasOption("intra")) { 83 | /* getting option intra */ 84 | run_intra = true; 85 | } 86 | 87 | run(considered, use_spark, run_intra); 88 | } catch (ParseException ex) { 89 | System.out.println(ex.getMessage()); 90 | new HelpFormatter().printHelp("ccc", options); 91 | } 92 | } 93 | 94 | private static void run(String[][] considered, boolean use_spark, boolean run_intra) throws IOException { 95 | List srcPaths = new LinkedList<>(); 96 | List classPaths = new LinkedList<>(); 97 | ConfigInterface configInterface = null; 98 | 99 | for (String[] cfg : considered) { 100 | srcPaths.addAll(Config.getSourcePaths(cfg)); 101 | classPaths.addAll(Config.getClassPaths(cfg)); 102 | configInterface = Config.getInterface(cfg); 103 | } 104 | 105 | // Run taint analysis 106 | ISourceSinkManager sourceSinkManager = new SourceSinkManager(configInterface); 107 | ITaintWrapper taintWrapper = TaintWrapper.getDefault(); 108 | TaintAnalysisDriver driver = new TaintAnalysisDriver(sourceSinkManager, taintWrapper); 109 | if (run_intra) { 110 | driver.runIntraTaintAnalysis(srcPaths, classPaths); 111 | } else { 112 | driver.runInterTaintAnalysis(srcPaths, classPaths, use_spark); 113 | } 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/assertion/Assert.java: -------------------------------------------------------------------------------- 1 | package assertion; 2 | 3 | public class Assert { 4 | 5 | private static void dieWithInfo() { 6 | dieWithInfo(""); 7 | } 8 | 9 | private static void dieWithInfo(String msg) { 10 | System.err.println("[ASSERT] Assert failure: " + msg); 11 | for (StackTraceElement ste : Thread.currentThread().getStackTrace()) { 12 | if (ste.getMethodName().compareTo("getStackTrace") == 0 || 13 | ste.getMethodName().compareTo("dieWithInfo") == 0 14 | ) { 15 | } else { 16 | System.err.println(ste); 17 | } 18 | } 19 | System.exit(-1); 20 | } 21 | 22 | public static void assertTrue(boolean value) { 23 | if (!value) dieWithInfo(); 24 | } 25 | 26 | public static void assertFalse(boolean value) { 27 | if (value) dieWithInfo(); 28 | } 29 | 30 | public static void assertNotEquals(int v1, int v2) { 31 | if (v1 == v2) dieWithInfo(); 32 | } 33 | 34 | public static void assertNotEquals(int v1, int v2, String msg) { 35 | if (v1 == v2) dieWithInfo(msg); 36 | } 37 | 38 | public static void assertEquals(int v1, int v2) { 39 | if (v1 != v2) dieWithInfo(); 40 | } 41 | 42 | public static void assertEquals(Object o1, Object o2) { 43 | if (o1 != o2) dieWithInfo(); 44 | } 45 | 46 | public static void assertNotEquals(Object o1, Object o2) { 47 | if (o1 == o2) dieWithInfo(); 48 | } 49 | 50 | public static void assertImpossible(String msg) { 51 | dieWithInfo(msg); 52 | } 53 | 54 | public static void assertNotNull(Object o) { 55 | if (o == null) dieWithInfo(); 56 | } 57 | 58 | public static void assertNull(Object o) { 59 | if (o != null) dieWithInfo(); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/configInterface/ConfigInterface.java: -------------------------------------------------------------------------------- 1 | package configInterface; 2 | 3 | import soot.Value; 4 | import soot.jimple.InvokeExpr; 5 | 6 | public interface ConfigInterface { 7 | 8 | boolean isGetter(InvokeExpr iexpr); 9 | 10 | boolean isSetter(InvokeExpr iexpr); 11 | 12 | String getConfigName(InvokeExpr iexpr); 13 | 14 | Value getDefaultValue(InvokeExpr iexpr); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/configInterface/HadoopInterface.java: -------------------------------------------------------------------------------- 1 | package configInterface; 2 | 3 | import java.util.List; 4 | 5 | import soot.SootClass; 6 | import soot.SootMethod; 7 | import soot.Value; 8 | import soot.jimple.Constant; 9 | import soot.jimple.InvokeExpr; 10 | import soot.jimple.StringConstant; 11 | 12 | public class HadoopInterface implements ConfigInterface { 13 | private static final String superConfigClass = "org.apache.hadoop.conf.Configuration"; 14 | 15 | @Override 16 | public boolean isGetter(InvokeExpr iexpr) { 17 | SootMethod callee = iexpr.getMethod(); 18 | if (isSubClass(callee.getDeclaringClass()) && 19 | callee.getName().startsWith("get")) { 20 | // System.out.println(callee.getName()); 21 | List args = iexpr.getArgs(); 22 | if (args.size() > 0 && args.get(0) instanceof StringConstant) { 23 | return true; 24 | } 25 | } 26 | return false; 27 | } 28 | 29 | @Override 30 | public boolean isSetter(InvokeExpr iexpr) { 31 | SootMethod callee = iexpr.getMethod(); 32 | if (isSubClass(callee.getDeclaringClass()) && 33 | callee.getName().startsWith("set")) { 34 | List args = iexpr.getArgs(); 35 | if (args.size() == 2 && args.get(0) instanceof StringConstant) { 36 | return true; 37 | } 38 | } 39 | return false; 40 | } 41 | 42 | @Override 43 | public String getConfigName(InvokeExpr iexpr) { 44 | if (iexpr.getArgCount() > 0 && 45 | iexpr.getArg(0) instanceof StringConstant) { 46 | Value name = iexpr.getArg(0); 47 | StringConstant strVal = (StringConstant) name; 48 | return strVal.value; 49 | } 50 | return null; 51 | } 52 | 53 | @Override 54 | public Value getDefaultValue(InvokeExpr iexpr) { 55 | if (iexpr.getArgCount() == 2 && 56 | iexpr.getArg(0) instanceof StringConstant && 57 | iexpr.getArg(1) instanceof Constant) { 58 | Value defaultValue = iexpr.getArg(1); 59 | return defaultValue; 60 | } 61 | return null; 62 | } 63 | 64 | private boolean isSubClass(SootClass cls) { 65 | if (cls.toString().contains(superConfigClass)) { 66 | return true; 67 | } 68 | if (cls.hasSuperclass() && cls.getSuperclass().toString().contains(superConfigClass)) { 69 | return true; 70 | } 71 | return false; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/configInterface/SparkInterface.java: -------------------------------------------------------------------------------- 1 | package configInterface; 2 | 3 | import soot.SootMethod; 4 | import soot.Value; 5 | import soot.jimple.Constant; 6 | import soot.jimple.InvokeExpr; 7 | import soot.jimple.StringConstant; 8 | 9 | import java.util.List; 10 | 11 | public class SparkInterface implements ConfigInterface { 12 | private static final String configClass = "org.apache.spark.SparkConf"; 13 | 14 | @Override 15 | public boolean isGetter(InvokeExpr iexpr) { 16 | SootMethod callee = iexpr.getMethod(); 17 | if (callee.getDeclaringClass().toString().contains(configClass) && 18 | callee.getName().startsWith("get")) { 19 | List args = iexpr.getArgs(); 20 | if (args.size() > 0 && args.get(0) instanceof StringConstant) { 21 | return true; 22 | } 23 | } 24 | return false; 25 | } 26 | 27 | @Override 28 | public boolean isSetter(InvokeExpr iexpr) { 29 | SootMethod callee = iexpr.getMethod(); 30 | if (callee.getDeclaringClass().toString().contains(configClass) && 31 | callee.getName().startsWith("set")) { 32 | List args = iexpr.getArgs(); 33 | if (args.size() == 2 && args.get(0) instanceof StringConstant) { 34 | return true; 35 | } 36 | } 37 | return false; 38 | } 39 | 40 | @Override 41 | public String getConfigName(InvokeExpr iexpr) { 42 | if (iexpr.getArgCount() > 0 && 43 | iexpr.getArg(0) instanceof StringConstant) { 44 | Value name = iexpr.getArg(0); 45 | StringConstant strVal = (StringConstant) name; 46 | return strVal.value; 47 | } 48 | return null; 49 | } 50 | 51 | @Override 52 | public Value getDefaultValue(InvokeExpr iexpr) { 53 | if (iexpr.getArgCount() == 2 && 54 | iexpr.getArg(0) instanceof StringConstant && 55 | iexpr.getArg(1) instanceof Constant) { 56 | Value defaultValue = iexpr.getArg(1); 57 | return defaultValue; 58 | } 59 | return null; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/configInterface/TestInterface.java: -------------------------------------------------------------------------------- 1 | package configInterface; 2 | 3 | import soot.SootMethod; 4 | import soot.Value; 5 | import soot.jimple.InvokeExpr; 6 | 7 | public class TestInterface implements ConfigInterface { 8 | 9 | @Override 10 | public boolean isGetter(InvokeExpr iexpr) { 11 | SootMethod callee = iexpr.getMethod(); 12 | if (callee.getName().contains("source")) { 13 | return true; 14 | } 15 | return false; 16 | } 17 | 18 | @Override 19 | public boolean isSetter(InvokeExpr iexpr) { 20 | return false; 21 | } 22 | 23 | @Override 24 | public String getConfigName(InvokeExpr iexpr) { 25 | return null; 26 | } 27 | 28 | @Override 29 | public Value getDefaultValue(InvokeExpr iexpr) { 30 | return null; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/taintAnalysis/InterAnalysisTransformer.java: -------------------------------------------------------------------------------- 1 | package taintAnalysis; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import soot.SceneTransformer; 6 | import soot.SootMethod; 7 | import taintAnalysis.sourceSinkManager.ISourceSinkManager; 8 | import taintAnalysis.taintWrapper.ITaintWrapper; 9 | import taintAnalysis.utility.PhantomIdentityStmt; 10 | import taintAnalysis.utility.PhantomRetStmt; 11 | 12 | import java.util.*; 13 | import java.util.concurrent.ExecutorService; 14 | import java.util.concurrent.Executors; 15 | 16 | public class InterAnalysisTransformer extends SceneTransformer { 17 | 18 | private final Logger logger = LoggerFactory.getLogger(getClass()); 19 | 20 | private final InterTaintAnalysis analysis; 21 | private boolean printResults = true; 22 | private Map>> pathsMap = new HashMap<>(); 23 | 24 | public InterAnalysisTransformer(ISourceSinkManager sourceSinkManager, ITaintWrapper taintWrapper) { 25 | this.analysis = new InterTaintAnalysis(sourceSinkManager, taintWrapper); 26 | } 27 | 28 | public List getSources() { 29 | return analysis.getSources(); 30 | } 31 | 32 | public Map>>> getMethodSummary() { 33 | return analysis.getMethodSummary(); 34 | } 35 | 36 | public Map> getMethodTaintCache() { 37 | return analysis.getMethodTaintCache(); 38 | } 39 | 40 | @Override 41 | protected void internalTransform(String phaseName, Map options) { 42 | analysis.doAnalysis(); 43 | 44 | Set sinks = new HashSet<>(); 45 | ArrayList sources = new ArrayList<>(analysis.getSources()); 46 | sources.sort(Comparator.comparing(Taint::toString)); 47 | 48 | // // For validation only 49 | // PathVisitor pv = new PathVisitor(); 50 | // for (Taint source : sources) { 51 | // pv.visit(source); 52 | // } 53 | 54 | int numOfThread = 5; 55 | logger.info("Reconstructing path using {} threads...", numOfThread); 56 | ExecutorService es = Executors.newFixedThreadPool(numOfThread); 57 | List todo = new ArrayList<>(sources.size()); 58 | for (Taint source : sources) { 59 | todo.add(new SourceSinkConnectionVisitor(source)); 60 | } 61 | try { 62 | es.invokeAll(todo); 63 | } catch (InterruptedException e) { 64 | e.printStackTrace(); 65 | } 66 | for (SourceSinkConnectionVisitor pv : todo) { 67 | pathsMap.put(pv.getSource(), pv.getPaths()); 68 | sinks.addAll(pv.getSinks()); 69 | } 70 | es.shutdown(); 71 | 72 | logger.info("Number of sinks reached by path reconstruction: {}", sinks.size()); 73 | 74 | if (printResults) { 75 | logger.info("Printing results..."); 76 | for (Taint source : sources) { 77 | System.out.println("Source: " + source + " reaches:\n"); 78 | List> paths = pathsMap.get(source); 79 | for (List path : paths) { 80 | System.out.println("-- Sink " + path.get(path.size() - 1) + " along:"); 81 | for (Taint t : path) { 82 | if (t.getStmt() instanceof PhantomIdentityStmt || 83 | t.getStmt() instanceof PhantomRetStmt) 84 | continue; 85 | System.out.println(" -> " + t); 86 | } 87 | System.out.println(); 88 | } 89 | System.out.println(); 90 | } 91 | } 92 | } 93 | 94 | public Map>> getPathsMap() { 95 | return pathsMap; 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/taintAnalysis/InterTaintAnalysis.java: -------------------------------------------------------------------------------- 1 | package taintAnalysis; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import soot.*; 6 | import taintAnalysis.sourceSinkManager.ISourceSinkManager; 7 | import taintAnalysis.taintWrapper.ITaintWrapper; 8 | 9 | import java.util.*; 10 | 11 | public class InterTaintAnalysis { 12 | 13 | private final Logger logger = LoggerFactory.getLogger(getClass()); 14 | 15 | private final ISourceSinkManager sourceSinkManager; 16 | private final ITaintWrapper taintWrapper; 17 | private final Set sources; 18 | private final Set sinks; 19 | private final Map>>> methodSummary; 20 | private final Map> methodTaintCache; 21 | 22 | public InterTaintAnalysis(ISourceSinkManager sourceSinkManager, ITaintWrapper taintWrapper) { 23 | this.sourceSinkManager = sourceSinkManager; 24 | this.taintWrapper = taintWrapper; 25 | this.sources = new HashSet<>(); 26 | this.sinks = new HashSet<>(); 27 | this.methodSummary = new HashMap<>(); 28 | this.methodTaintCache = new HashMap<>(); 29 | } 30 | 31 | public void doAnalysis() { 32 | this.sources.clear(); 33 | this.sinks.clear(); 34 | this.methodSummary.clear(); 35 | this.methodTaintCache.clear(); 36 | 37 | List methodList = new ArrayList<>(); 38 | for (SootClass sc : Scene.v().getApplicationClasses()) { 39 | for (SootMethod sm : sc.getMethods()) { 40 | if (sm.isConcrete()) { 41 | methodList.add(sm); 42 | } 43 | } 44 | } 45 | methodList.sort(Comparator.comparing(SootMethod::toString)); 46 | 47 | logger.info("Num of methods: {}", methodList.size()); 48 | 49 | // Bootstrap 50 | int iter = 1; 51 | logger.info("iter {}", iter); 52 | List bodyList = new ArrayList<>(); 53 | for (SootMethod sm : methodList) { 54 | Body b = sm.retrieveActiveBody(); 55 | bodyList.add(b); 56 | } 57 | for (Body b : bodyList) { 58 | TaintFlowAnalysis analysis = new TaintFlowAnalysis(b, sourceSinkManager, Taint.getEmptyTaint(), 59 | methodSummary, methodTaintCache, taintWrapper); 60 | analysis.doAnalysis(); 61 | sources.addAll(analysis.getSources()); 62 | } 63 | iter++; 64 | 65 | boolean changed = true; 66 | while (changed) { 67 | changed = false; 68 | logger.info("iter {}", iter); 69 | 70 | for (SootMethod sm : methodList) { 71 | Body b = sm.retrieveActiveBody(); 72 | Set entryTaints = new HashSet<>(); 73 | entryTaints.addAll(methodSummary.get(sm).keySet()); 74 | for (Taint entryTaint : entryTaints) { 75 | TaintFlowAnalysis analysis = new TaintFlowAnalysis(b, sourceSinkManager, entryTaint, 76 | methodSummary, methodTaintCache, taintWrapper); 77 | analysis.doAnalysis(); 78 | sinks.addAll(analysis.getSinks()); 79 | changed |= analysis.isChanged(); 80 | } 81 | } 82 | 83 | iter++; 84 | } 85 | 86 | logger.info("Found {} sinks reached from {} sources", sinks.size(), sources.size()); 87 | } 88 | 89 | public List getSources() { 90 | List lst = new ArrayList<>(); 91 | lst.addAll(sources); 92 | return lst; 93 | } 94 | 95 | public List getSinks() { 96 | List lst = new ArrayList<>(); 97 | lst.addAll(sinks); 98 | return lst; 99 | } 100 | 101 | public Map>>> getMethodSummary() { 102 | return methodSummary; 103 | } 104 | 105 | public Map> getMethodTaintCache() { 106 | return methodTaintCache; 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/taintAnalysis/IntraAnalysisTransformer.java: -------------------------------------------------------------------------------- 1 | package taintAnalysis; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import soot.Body; 6 | import soot.BodyTransformer; 7 | import soot.SootMethod; 8 | import taintAnalysis.sourceSinkManager.ISourceSinkManager; 9 | import taintAnalysis.taintWrapper.ITaintWrapper; 10 | 11 | import java.util.*; 12 | 13 | public class IntraAnalysisTransformer extends BodyTransformer { 14 | 15 | private final Logger logger = LoggerFactory.getLogger(getClass()); 16 | 17 | private final ISourceSinkManager sourceSinkManager; 18 | private final ITaintWrapper taintWrapper; 19 | private final List> sourceLists; 20 | private final Map>>> methodSummary; 21 | private final Map> methodTaintCache; 22 | 23 | public IntraAnalysisTransformer(ISourceSinkManager sourceSinkManager, ITaintWrapper taintWrapper) { 24 | this.sourceSinkManager = sourceSinkManager; 25 | this.taintWrapper = taintWrapper; 26 | this.sourceLists = Collections.synchronizedList(new ArrayList<>()); 27 | this.methodSummary = new HashMap<>(); 28 | this.methodTaintCache = new HashMap<>(); 29 | } 30 | 31 | public List> getSourceLists() { 32 | return sourceLists; 33 | } 34 | 35 | public Map>>> getMethodSummary() { 36 | return methodSummary; 37 | } 38 | 39 | public Map> getMethodTaintCache() { 40 | return methodTaintCache; 41 | } 42 | 43 | @Override 44 | protected void internalTransform(Body b, String phaseName, Map options) { 45 | TaintFlowAnalysis analysis = 46 | new TaintFlowAnalysis(b, sourceSinkManager, Taint.getEmptyTaint(), 47 | methodSummary, methodTaintCache, taintWrapper); 48 | analysis.doAnalysis(); 49 | 50 | List lst = new ArrayList<>(); 51 | lst.addAll(analysis.getSources()); 52 | sourceLists.add(lst); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/taintAnalysis/PathVisitor.java: -------------------------------------------------------------------------------- 1 | package taintAnalysis; 2 | 3 | import soot.SootMethod; 4 | import soot.jimple.Stmt; 5 | import taintAnalysis.utility.PhantomRetStmt; 6 | 7 | import java.util.*; 8 | 9 | public class PathVisitor { 10 | 11 | private final long threshold; 12 | private long cnt; 13 | 14 | public PathVisitor() { 15 | this(20000); 16 | } 17 | 18 | public PathVisitor(long threshold) { 19 | this.threshold = threshold; 20 | } 21 | 22 | public void visit(Taint t) { 23 | cnt = 0; 24 | Set sinks = new HashSet<>(); 25 | Stack callerStack = new Stack<>(); 26 | Set methodSet = new HashSet<>(); 27 | methodSet.add(t.getMethod()); 28 | Stack> visitedStack = new Stack<>(); 29 | visitedStack.push(new HashSet<>()); 30 | System.out.println("source: " + t); 31 | dfs(t, 0, callerStack, methodSet, visitedStack, sinks); 32 | System.out.println("Number of paths: " + cnt); 33 | System.out.println(); 34 | } 35 | 36 | private void dfs(Taint t, int depth, Stack callerStack, Set methodSet, 37 | Stack> visitedStack, Set sinks) { 38 | if (cnt > threshold) { 39 | return; 40 | } 41 | Set visited = visitedStack.peek(); 42 | if (visited.contains(t)) { 43 | return; 44 | } 45 | visited.add(t); 46 | 47 | for (int i = 0; i < depth; i++) { 48 | System.out.print("-"); 49 | } 50 | System.out.println(t); 51 | 52 | if (t.isSink()) { 53 | sinks.add(t); 54 | } 55 | 56 | boolean isEndPoint = true; 57 | Stmt currStmt = t.getStmt(); 58 | ArrayList successors = new ArrayList<>(t.getSuccessors()); 59 | successors.sort(Comparator.comparing(Taint::toString)); 60 | for (Taint successor : successors) { 61 | if (t.getTransferType() == Taint.TransferType.Call) { 62 | // Visit callee 63 | SootMethod callee = successor.getMethod(); 64 | if (!methodSet.contains(callee)) { 65 | callerStack.push(currStmt); 66 | methodSet.add(callee); 67 | visitedStack.push(new HashSet<>()); 68 | isEndPoint = false; 69 | dfs(successor, depth+1, callerStack, methodSet, visitedStack, sinks); 70 | visitedStack.pop(); 71 | methodSet.remove(callee); 72 | callerStack.pop(); 73 | } 74 | } else if (currStmt instanceof PhantomRetStmt) { 75 | SootMethod callee = t.getMethod(); 76 | SootMethod caller = successor.getMethod(); 77 | if (!callerStack.isEmpty()) { 78 | // Return to the previous callee 79 | Stmt callSite = callerStack.peek(); 80 | if (callSite == successor.getStmt()) { 81 | callerStack.pop(); 82 | methodSet.remove(callee); 83 | visitedStack.pop(); 84 | isEndPoint = false; 85 | dfs(successor, depth+1, callerStack, methodSet, visitedStack, sinks); 86 | visitedStack.push(visited); 87 | methodSet.add(callee); 88 | callerStack.push(callSite); 89 | } 90 | } else { 91 | // Return to an unexplored caller 92 | if (!methodSet.contains(caller)) { 93 | methodSet.add(caller); 94 | visitedStack.push(new HashSet<>()); 95 | isEndPoint = false; 96 | dfs(successor, depth+1, callerStack, methodSet, visitedStack, sinks); 97 | visitedStack.pop(); 98 | methodSet.remove(caller); 99 | } 100 | } 101 | } else { 102 | // Visit within the same method 103 | isEndPoint = false; 104 | dfs(successor, depth+1, callerStack, methodSet, visitedStack, sinks); 105 | } 106 | } 107 | if (isEndPoint) { 108 | cnt++; 109 | } 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/taintAnalysis/SourceSinkConnectionVisitor.java: -------------------------------------------------------------------------------- 1 | package taintAnalysis; 2 | 3 | import soot.SootMethod; 4 | import soot.jimple.Stmt; 5 | import taintAnalysis.utility.PhantomRetStmt; 6 | 7 | import java.util.*; 8 | import java.util.concurrent.Callable; 9 | 10 | public class SourceSinkConnectionVisitor implements Callable { 11 | 12 | private final Taint source; 13 | private final long threshold; 14 | private final Set sinks; 15 | private final List> paths; 16 | 17 | private long cnt; 18 | 19 | public SourceSinkConnectionVisitor(Taint source) { 20 | this(source, 20000); 21 | } 22 | 23 | public SourceSinkConnectionVisitor(Taint source, long threshold) { 24 | this.source = source; 25 | this.threshold = threshold; 26 | this.sinks = new HashSet<>(); 27 | this.paths = new ArrayList<>(); 28 | } 29 | 30 | @Override 31 | public Object call() throws Exception { 32 | visit(source); 33 | return null; 34 | } 35 | 36 | public void visit(Taint t) { 37 | cnt = 0; 38 | Stack intermediatePath = new Stack<>(); 39 | Stack callerStack = new Stack<>(); 40 | Set methodSet = new HashSet<>(); 41 | methodSet.add(t.getMethod()); 42 | Stack> visitedStack = new Stack<>(); 43 | visitedStack.push(new HashSet<>()); 44 | dfs(t, callerStack, methodSet, visitedStack, sinks, intermediatePath, paths); 45 | } 46 | 47 | private void dfs(Taint t, Stack callerStack, Set methodSet, 48 | Stack> visitedStack, Set sinks, 49 | Stack intermediatePath, List> paths) { 50 | if (cnt > threshold) { 51 | return; 52 | } 53 | Set visited = visitedStack.peek(); 54 | if (visited.contains(t)) { 55 | return; 56 | } 57 | visited.add(t); 58 | intermediatePath.push(t); 59 | 60 | if (t.isSink()) { 61 | if (!sinks.contains(t)) { 62 | sinks.add(t); 63 | paths.add(new ArrayList(intermediatePath)); 64 | } 65 | } 66 | 67 | boolean isEndPoint = true; 68 | Stmt currStmt = t.getStmt(); 69 | ArrayList successors = new ArrayList<>(t.getSuccessors()); 70 | successors.sort(Comparator.comparing(Taint::toString)); 71 | for (Taint successor : successors) { 72 | if (t.getTransferType() == Taint.TransferType.Call) { 73 | // Visit callee 74 | SootMethod callee = successor.getMethod(); 75 | if (!methodSet.contains(callee)) { 76 | callerStack.push(currStmt); 77 | methodSet.add(callee); 78 | visitedStack.push(new HashSet<>()); 79 | isEndPoint = false; 80 | dfs(successor, callerStack, methodSet, visitedStack, sinks, intermediatePath, paths); 81 | visitedStack.pop(); 82 | methodSet.remove(callee); 83 | callerStack.pop(); 84 | } 85 | } else if (currStmt instanceof PhantomRetStmt) { 86 | SootMethod callee = t.getMethod(); 87 | SootMethod caller = successor.getMethod(); 88 | if (!callerStack.isEmpty()) { 89 | // Return to the previous callee 90 | Stmt callSite = callerStack.peek(); 91 | if (callSite == successor.getStmt()) { 92 | callerStack.pop(); 93 | methodSet.remove(callee); 94 | visitedStack.pop(); 95 | isEndPoint = false; 96 | dfs(successor, callerStack, methodSet, visitedStack, sinks, intermediatePath, paths); 97 | visitedStack.push(visited); 98 | methodSet.add(callee); 99 | callerStack.push(callSite); 100 | } 101 | } else { 102 | // Return to an unexplored caller 103 | if (!methodSet.contains(caller)) { 104 | methodSet.add(caller); 105 | visitedStack.push(new HashSet<>()); 106 | isEndPoint = false; 107 | dfs(successor, callerStack, methodSet, visitedStack, sinks, intermediatePath, paths); 108 | visitedStack.pop(); 109 | methodSet.remove(caller); 110 | } 111 | } 112 | } else { 113 | // Visit within the same method 114 | isEndPoint = false; 115 | dfs(successor, callerStack, methodSet, visitedStack, sinks, intermediatePath, paths); 116 | } 117 | } 118 | if (isEndPoint) { 119 | cnt++; 120 | } 121 | 122 | intermediatePath.pop(); 123 | } 124 | 125 | public long getThreshold() { 126 | return threshold; 127 | } 128 | 129 | public Set getSinks() { 130 | return sinks; 131 | } 132 | 133 | public Taint getSource() { 134 | return source; 135 | } 136 | 137 | public List> getPaths() { 138 | return paths; 139 | } 140 | 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/taintAnalysis/Taint.java: -------------------------------------------------------------------------------- 1 | package taintAnalysis; 2 | 3 | import soot.*; 4 | import soot.jimple.*; 5 | 6 | import java.util.HashSet; 7 | import java.util.Map; 8 | import java.util.Objects; 9 | import java.util.Set; 10 | 11 | public class Taint { 12 | 13 | public enum TransferType { 14 | None, 15 | Call, 16 | Return 17 | } 18 | 19 | private static final Taint emptyTaint = new Taint(null, null, null); 20 | 21 | private final Value plainValue; 22 | private final SootField field; 23 | private final Stmt stmt; 24 | private final SootMethod method; 25 | private final Set successors; 26 | private final TransferType transferType; 27 | private boolean isSink = false; 28 | 29 | public static Taint getEmptyTaint() { 30 | return emptyTaint; 31 | } 32 | 33 | /** 34 | * Gets a globally unique taint object for a given pair of value and its statement context. 35 | * The whole value is tainted, whose taint is transferred from another taint object (can be null). 36 | * 37 | * @param t the taint from which to transfer (null when a new taint is created) 38 | * @param v the value which the taint is on 39 | * @param stmt the statement context of the taint 40 | * @param method the method context of the taint 41 | * @param taintCache the taint cache of the method into which the taint is transferred, 42 | * used to ensure global uniqueness 43 | * @return The corresponding globally unique taint object 44 | */ 45 | public static Taint getTaintFor(Taint t, Value v, Stmt stmt, SootMethod method, 46 | Map taintCache) { 47 | Taint newTaint = new Taint(v, stmt, method); 48 | if (taintCache.containsKey(newTaint)) { 49 | newTaint = taintCache.get(newTaint); 50 | } else { 51 | taintCache.put(newTaint, newTaint); 52 | } 53 | if (t != null) { 54 | t.addSuccessor(newTaint); 55 | } 56 | return newTaint; 57 | } 58 | 59 | /** 60 | * Gets a globally unique taint object whose taint is transferred from another taint object 61 | * with method context transfer type None. 62 | * 63 | * @param t the taint from which to transfer 64 | * @param v the value which the taint is on 65 | * @param stmt the statement context of the taint 66 | * @param method the method context of the taint 67 | * @param taintCache the taint cache of the method into which the taint is transferred, 68 | * used to ensure global uniqueness 69 | * @return The corresponding globally unique taint object after transfer 70 | */ 71 | public static Taint getTransferredTaintFor(Taint t, Value v, Stmt stmt, SootMethod method, 72 | Map taintCache) { 73 | return getTransferredTaintFor(t, v, stmt, method, taintCache, TransferType.None); 74 | } 75 | 76 | /** 77 | * Gets a globally unique taint object whose taint is transferred from another taint object, 78 | * the method context transfer type is to indicate taint transfer along call/return edges. 79 | * 80 | * @param t the taint from which to transfer 81 | * @param v the value which the taint is on 82 | * @param stmt the statement context of the taint 83 | * @param method the method context of the taint 84 | * @param taintCache the taint cache of the method into which the taint is transferred, 85 | * used to ensure global uniqueness 86 | * @param transferType the type of method context transfer 87 | * @return The corresponding globally unique taint object after transfer 88 | */ 89 | public static Taint getTransferredTaintFor(Taint t, Value v, Stmt stmt, SootMethod method, 90 | Map taintCache, TransferType transferType) { 91 | Taint newTaint = new Taint(t, v, stmt, method, transferType); 92 | if (taintCache.containsKey(newTaint)) { 93 | newTaint = taintCache.get(newTaint); 94 | } else { 95 | taintCache.put(newTaint, newTaint); 96 | } 97 | t.addSuccessor(newTaint); 98 | return newTaint; 99 | } 100 | 101 | public boolean taints(Value v) { 102 | // Empty taint doesn't taint anything 103 | if (isEmpty()) return false; 104 | 105 | // Taint on V must taint V, taint on B.* also taints B 106 | if (plainValue.equivTo(v)) return true; 107 | 108 | if (v instanceof Expr) { 109 | return taints((Expr) v); 110 | } 111 | if (v instanceof Ref) { 112 | return taints((Ref) v); 113 | } 114 | return false; 115 | } 116 | 117 | private boolean taints(Expr e) { 118 | if (e instanceof BinopExpr) { 119 | BinopExpr binopExpr = (BinopExpr) e; 120 | Value op1 = binopExpr.getOp1(); 121 | Value op2 = binopExpr.getOp2(); 122 | return taints(op1) || taints(op2); 123 | } 124 | if (e instanceof UnopExpr) { 125 | Value op = ((UnopExpr) e).getOp(); 126 | return taints(op); 127 | } 128 | if (e instanceof CastExpr) { 129 | Value op = ((CastExpr) e).getOp(); 130 | return taints(op); 131 | } 132 | if (e instanceof InstanceOfExpr) { 133 | Value op = ((InstanceOfExpr) e).getOp(); 134 | return taints(op); 135 | } 136 | return false; 137 | } 138 | 139 | private boolean taints(Ref r) { 140 | if (r instanceof InstanceFieldRef) { 141 | InstanceFieldRef fieldRef = (InstanceFieldRef) r; 142 | if (field == null) return false; 143 | return plainValue.equivTo(fieldRef.getBase()) && field.equals(fieldRef.getField()); 144 | } 145 | if (r instanceof ArrayRef) { 146 | ArrayRef arrayRef = (ArrayRef) r; 147 | return plainValue.equivTo(arrayRef.getBase()); 148 | } 149 | // static field ref not supported 150 | return false; 151 | } 152 | 153 | private Taint(Value value, Stmt stmt, SootMethod method) { 154 | this(null, value, stmt, method, TransferType.None); 155 | } 156 | 157 | private Taint(Taint transferFrom, Value value, Stmt stmt, SootMethod method, TransferType transferType) { 158 | this.stmt = stmt; 159 | this.method = method; 160 | this.successors = new HashSet<>(); 161 | this.transferType = transferType; 162 | 163 | if (value instanceof Ref) { 164 | // if value is of ref type, ignore the taint from which to transfer 165 | if (value instanceof InstanceFieldRef) { 166 | InstanceFieldRef fieldRef = (InstanceFieldRef) value; 167 | this.plainValue = fieldRef.getBase(); 168 | this.field = fieldRef.getField(); 169 | } else { 170 | // array ref and static field ref is not currently supported, 171 | // just taint the entire value 172 | this.plainValue = value; 173 | this.field = null; 174 | } 175 | } else if (transferFrom != null) { 176 | // for a non-ref object-typed value, transfer taint from t 177 | this.plainValue = value; 178 | this.field = transferFrom.getField(); 179 | } else { 180 | this.plainValue = value; 181 | this.field = null; 182 | } 183 | } 184 | 185 | public boolean isEmpty() { 186 | return plainValue == null; 187 | } 188 | 189 | public Value getPlainValue() { 190 | return plainValue; 191 | } 192 | 193 | public SootField getField() { 194 | return field; 195 | } 196 | 197 | public Stmt getStmt() { 198 | return stmt; 199 | } 200 | 201 | public SootMethod getMethod() { 202 | return method; 203 | } 204 | 205 | public Set getSuccessors() { 206 | return successors; 207 | } 208 | 209 | public void addSuccessor(Taint successor) { 210 | this.successors.add(successor); 211 | } 212 | 213 | public TransferType getTransferType() { 214 | return transferType; 215 | } 216 | 217 | public boolean isSink() { 218 | return isSink; 219 | } 220 | 221 | public void setSink() { 222 | isSink = true; 223 | } 224 | 225 | @Override 226 | public String toString() { 227 | if (isEmpty()) return "Empty Taint"; 228 | 229 | String str = ""; 230 | if (transferType != TransferType.None) { 231 | str += "[" + transferType + "] "; 232 | } 233 | str += plainValue + (field != null ? "." + field : "") + 234 | " in " + stmt + " in method " + method; 235 | 236 | return str; 237 | } 238 | 239 | @Override 240 | public boolean equals(Object o) { 241 | if (this == o) return true; 242 | if (o == null || getClass() != o.getClass()) return false; 243 | Taint taint = (Taint) o; 244 | return Objects.equals(plainValue, taint.plainValue) && 245 | Objects.equals(field, taint.field) && 246 | Objects.equals(stmt, taint.stmt) && 247 | Objects.equals(method, taint.method) && 248 | transferType == taint.transferType; 249 | } 250 | 251 | @Override 252 | public int hashCode() { 253 | return Objects.hash(plainValue, field, stmt, method, transferType); 254 | } 255 | 256 | } 257 | -------------------------------------------------------------------------------- /src/main/java/taintAnalysis/TaintAnalysisDriver.java: -------------------------------------------------------------------------------- 1 | package taintAnalysis; 2 | 3 | import soot.G; 4 | import soot.PackManager; 5 | import soot.Transform; 6 | import taintAnalysis.sourceSinkManager.ISourceSinkManager; 7 | import taintAnalysis.taintWrapper.ITaintWrapper; 8 | 9 | import java.util.List; 10 | 11 | public class TaintAnalysisDriver { 12 | 13 | private ISourceSinkManager sourceSinkManager; 14 | private ITaintWrapper taintWrapper; 15 | 16 | public TaintAnalysisDriver(ISourceSinkManager sourceSinkManager) { 17 | this(sourceSinkManager, null); 18 | } 19 | 20 | public TaintAnalysisDriver(ISourceSinkManager sourceSinkManager, ITaintWrapper taintWrapper) { 21 | this.sourceSinkManager = sourceSinkManager; 22 | this.taintWrapper = taintWrapper; 23 | } 24 | 25 | public IntraAnalysisTransformer runIntraTaintAnalysis(List srcPaths, List classPaths) { 26 | G.reset(); 27 | 28 | String classPath = String.join(":", classPaths); 29 | String[] initArgs = { 30 | // Input Options 31 | "-cp", classPath, 32 | "-pp", 33 | "-allow-phantom-refs", 34 | "-no-bodies-for-excluded", 35 | 36 | // Output Options 37 | "-f", "J", 38 | }; 39 | 40 | String[] sootArgs = new String[initArgs.length + 2 * srcPaths.size()]; 41 | for (int i = 0; i < initArgs.length; i++) { 42 | sootArgs[i] = initArgs[i]; 43 | } 44 | for (int i = 0; i < srcPaths.size(); i++) { 45 | sootArgs[initArgs.length + 2*i] = "-process-dir"; 46 | sootArgs[initArgs.length + 2*i + 1] = srcPaths.get(i); 47 | } 48 | 49 | PackManager.v().getPack("jtp").add( 50 | new Transform("jtp.taintanalysis", new IntraAnalysisTransformer(sourceSinkManager, taintWrapper))); 51 | 52 | soot.Main.main(sootArgs); 53 | 54 | IntraAnalysisTransformer transformer = (IntraAnalysisTransformer) 55 | PackManager.v().getPack("jtp").get("jtp.taintanalysis").getTransformer(); 56 | return transformer; 57 | } 58 | 59 | public InterAnalysisTransformer runInterTaintAnalysis(List srcPaths, List classPaths, boolean use_spark) { 60 | G.reset(); 61 | 62 | String classPath = String.join(":", classPaths); 63 | String[] initArgs; 64 | if (use_spark) { 65 | initArgs = new String[]{ 66 | // General Options 67 | "-w", 68 | 69 | // Input Options 70 | "-cp", classPath, 71 | "-pp", 72 | "-allow-phantom-refs", 73 | "-no-bodies-for-excluded", 74 | 75 | // Output Options 76 | "-f", "J", 77 | 78 | // Phase Options 79 | "-p", "cg", "all-reachable", 80 | "-p", "cg.spark", "enabled", 81 | "-p", "cg.spark", "apponly" 82 | }; 83 | } else { 84 | initArgs = new String[]{ 85 | // General Options 86 | "-w", 87 | 88 | // Input Options 89 | "-cp", classPath, 90 | "-pp", 91 | "-allow-phantom-refs", 92 | "-no-bodies-for-excluded", 93 | 94 | // Output Options 95 | "-f", "J", 96 | 97 | // Phase Options 98 | "-p", "cg", "off" 99 | }; 100 | } 101 | 102 | String[] sootArgs = new String[initArgs.length + 2 * srcPaths.size()]; 103 | for (int i = 0; i < initArgs.length; i++) { 104 | sootArgs[i] = initArgs[i]; 105 | } 106 | for (int i = 0; i < srcPaths.size(); i++) { 107 | sootArgs[initArgs.length + 2*i] = "-process-dir"; 108 | sootArgs[initArgs.length + 2*i + 1] = srcPaths.get(i); 109 | } 110 | 111 | PackManager.v().getPack("wjtp").add( 112 | new Transform("wjtp.taintanalysis", new InterAnalysisTransformer(sourceSinkManager, taintWrapper))); 113 | 114 | soot.Main.main(sootArgs); 115 | 116 | InterAnalysisTransformer transformer = (InterAnalysisTransformer) 117 | PackManager.v().getPack("wjtp").get("wjtp.taintanalysis").getTransformer(); 118 | return transformer; 119 | } 120 | 121 | public ISourceSinkManager getSourceSinkManager() { 122 | return sourceSinkManager; 123 | } 124 | 125 | public void setSourceSinkManager(ISourceSinkManager sourceSinkManager) { 126 | this.sourceSinkManager = sourceSinkManager; 127 | } 128 | 129 | public ITaintWrapper getTaintWrapper() { 130 | return taintWrapper; 131 | } 132 | 133 | public void setTaintWrapper(ITaintWrapper taintWrapper) { 134 | this.taintWrapper = taintWrapper; 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/taintAnalysis/TaintFlowAnalysis.java: -------------------------------------------------------------------------------- 1 | package taintAnalysis; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import soot.*; 6 | import soot.jimple.*; 7 | import soot.jimple.toolkits.callgraph.CallGraph; 8 | import soot.jimple.toolkits.callgraph.Edge; 9 | import soot.toolkits.graph.ExceptionalUnitGraph; 10 | import soot.toolkits.scalar.ForwardFlowAnalysis; 11 | import taintAnalysis.sourceSinkManager.ISourceSinkManager; 12 | import taintAnalysis.taintWrapper.ITaintWrapper; 13 | import taintAnalysis.utility.PhantomIdentityStmt; 14 | import taintAnalysis.utility.PhantomRetStmt; 15 | 16 | import java.util.*; 17 | 18 | import static assertion.Assert.assertNotNull; 19 | 20 | public class TaintFlowAnalysis extends ForwardFlowAnalysis> { 21 | 22 | private final Logger logger = LoggerFactory.getLogger(getClass()); 23 | 24 | private static final CallGraph cg = Scene.v().hasCallGraph() ? Scene.v().getCallGraph() : null; 25 | 26 | private boolean changed = false; 27 | private final Body body; 28 | private final SootMethod method; 29 | private final ISourceSinkManager sourceSinkManager; 30 | private final ITaintWrapper taintWrapper; 31 | private final Taint entryTaint; 32 | private final Map>>> methodSummary; 33 | private final Map>> currMethodSummary; 34 | private final Map> methodTaintCache; 35 | private final Map currTaintCache; 36 | private final PhantomRetStmt phantomRetStmt; 37 | private final Set sources; 38 | private final Set sinks; 39 | 40 | public TaintFlowAnalysis(Body body, ISourceSinkManager sourceSinkManager) { 41 | this(body, sourceSinkManager, Taint.getEmptyTaint(), new HashMap<>(), new HashMap<>(), null); 42 | } 43 | 44 | public TaintFlowAnalysis(Body body, 45 | ISourceSinkManager sourceSinkManager, 46 | Taint entryTaint, 47 | Map>>> methodSummary, 48 | Map> methodTaintCache, 49 | ITaintWrapper taintWrapper) { 50 | super(new ExceptionalUnitGraph(body)); 51 | this.body = body; 52 | this.method = body.getMethod(); 53 | this.sourceSinkManager = sourceSinkManager; 54 | this.entryTaint = entryTaint; 55 | this.methodSummary = methodSummary; 56 | this.methodTaintCache = methodTaintCache; 57 | this.sources = new HashSet<>(); 58 | this.sinks = new HashSet<>(); 59 | this.taintWrapper = taintWrapper; 60 | this.phantomRetStmt = PhantomRetStmt.getInstance(method); 61 | 62 | // Sanity check 63 | assertNotNull(body); 64 | assertNotNull(sourceSinkManager); 65 | assertNotNull(entryTaint); 66 | assertNotNull(methodSummary); 67 | assertNotNull(methodTaintCache); 68 | 69 | // Initialize methodSummary and methodTaintCache for current method (if not done yet) 70 | methodSummary.putIfAbsent(method, new HashMap<>()); 71 | this.currMethodSummary = methodSummary.get(method); 72 | methodTaintCache.putIfAbsent(method, new HashMap<>()); 73 | this.currTaintCache = methodTaintCache.get(method); 74 | 75 | // Initialize the taint summary for current method with the input entry taint (if not done yet) 76 | // Summary list format: idx 0: (set of taints on) base, 1: retVal, 2+: parameters 77 | if (!this.currMethodSummary.containsKey(entryTaint)) { 78 | changed = true; 79 | List> summary = new ArrayList<>(); 80 | for (int i = 0; i < method.getParameterCount() + 2; i++) { 81 | summary.add(new HashSet<>()); 82 | } 83 | this.currMethodSummary.put(entryTaint, summary); 84 | } 85 | } 86 | 87 | public boolean isChanged() { 88 | return changed; 89 | } 90 | 91 | public Set getSources() { 92 | return sources; 93 | } 94 | 95 | public Set getSinks() { 96 | return sinks; 97 | } 98 | 99 | public void doAnalysis() { 100 | logger.debug("Analyzing method {}", method); 101 | super.doAnalysis(); 102 | } 103 | 104 | @Override 105 | protected void flowThrough(Set in, Unit unit, Set out) { 106 | out.clear(); 107 | out.addAll(in); 108 | 109 | Stmt stmt = (Stmt) unit; 110 | 111 | if (stmt instanceof AssignStmt) { 112 | visitAssign(in, (AssignStmt) stmt, out); 113 | } 114 | 115 | if (stmt instanceof InvokeStmt) { 116 | InvokeExpr invoke = stmt.getInvokeExpr(); 117 | if (!sourceSinkManager.isSource(stmt)) { 118 | visitInvoke(in, stmt, invoke, out); 119 | } 120 | } 121 | 122 | if (stmt instanceof ReturnStmt || stmt instanceof ReturnVoidStmt) { 123 | visitReturn(in, stmt); 124 | } 125 | 126 | if (sourceSinkManager.isSink(stmt)) { 127 | visitSink(in, stmt); 128 | } 129 | } 130 | 131 | private void visitAssign(Set in, AssignStmt stmt, Set out) { 132 | Value leftOp = stmt.getLeftOp(); 133 | Value rightOp = stmt.getRightOp(); 134 | 135 | // KILL 136 | for (Taint t : in) { 137 | if (t.taints(leftOp)) { 138 | out.remove(t); 139 | } 140 | } 141 | 142 | // GEN 143 | if (stmt.containsInvokeExpr()) { 144 | InvokeExpr invoke = stmt.getInvokeExpr(); 145 | if (sourceSinkManager.isSource(stmt)) { 146 | Taint newTaint = Taint.getTaintFor(null, leftOp, stmt, method, currTaintCache); 147 | sources.add(newTaint); 148 | out.add(newTaint); 149 | } else { 150 | visitInvoke(in, stmt, invoke, out); 151 | } 152 | } else { 153 | for (Taint t : in) { 154 | if (t.taints(rightOp)) { 155 | Taint newTaint; 156 | if (leftOp.getType() instanceof PrimType || rightOp instanceof InstanceFieldRef) { 157 | newTaint = Taint.getTaintFor(t, leftOp, stmt, method, currTaintCache); 158 | } else { 159 | newTaint = Taint.getTransferredTaintFor( 160 | t, leftOp, stmt, method, currTaintCache, Taint.TransferType.None); 161 | } 162 | out.add(newTaint); 163 | } 164 | } 165 | } 166 | } 167 | 168 | private void visitInvoke(Set in, Stmt stmt, InvokeExpr invoke, Set out) { 169 | SootMethod calleeMethod = invoke.getMethod(); 170 | assertNotNull(calleeMethod); 171 | 172 | // Check if taint wrapper applies 173 | if (taintWrapper != null && taintWrapper.supportsCallee(calleeMethod)) { 174 | Set killSet = new HashSet<>(); 175 | Set genSet = new HashSet<>(); 176 | taintWrapper.genTaintsForMethodInternal(in, stmt, method, killSet, genSet, currTaintCache); 177 | for (Taint t : killSet) { 178 | out.remove(t); 179 | } 180 | for (Taint t : genSet) { 181 | out.add(t); 182 | } 183 | return; 184 | } 185 | 186 | // Get all possible callees for this call site 187 | List methods = new ArrayList<>(); 188 | methods.add(calleeMethod); 189 | if (cg != null) { 190 | for (Iterator it = cg.edgesOutOf(stmt); it.hasNext(); ) { 191 | Edge edge = it.next(); 192 | SootMethod sm = edge.tgt(); 193 | if (calleeMethod.getName().equals(sm.getName())) { 194 | methods.add(sm); 195 | } 196 | } 197 | } 198 | 199 | // Get the base object of this invocation in caller (if applies) 200 | Value base = null; 201 | if (invoke instanceof InstanceInvokeExpr) { 202 | base = ((InstanceInvokeExpr) invoke).getBase(); 203 | } 204 | 205 | // Get the retVal of this invocation in caller (if applies) 206 | Value retVal = null; 207 | if (stmt instanceof AssignStmt) { 208 | retVal = ((AssignStmt) stmt).getLeftOp(); 209 | } 210 | 211 | // Compute KILL and GEN 212 | List> killSets = new ArrayList<>(); 213 | List> genSets = new ArrayList<>(); 214 | for (SootMethod callee : methods) { 215 | if (!callee.hasActiveBody()) { 216 | logger.debug("No active body for callee {} in {}", callee, method); 217 | continue; 218 | } 219 | Body calleeBody = callee.getActiveBody(); 220 | 221 | Set killSet = new HashSet<>(); 222 | Set genSet = new HashSet<>(); 223 | killSets.add(killSet); 224 | genSets.add(genSet); 225 | 226 | // Get this object in callee (if exists) 227 | Value calleeThisLocal = null; 228 | if (invoke instanceof InstanceInvokeExpr) { 229 | calleeThisLocal = calleeBody.getThisLocal(); 230 | } 231 | 232 | // Initialize methodSummary and methodTaintCache for callee (if not done yet) 233 | methodSummary.putIfAbsent(callee, new HashMap<>()); 234 | Map>> calleeSummary = methodSummary.get(callee); 235 | methodTaintCache.putIfAbsent(callee, new HashMap<>()); 236 | Map calleeTaintCache = methodTaintCache.get(callee); 237 | 238 | // Initialize the empty taint summary for callee (if not done yet) 239 | // Summary list format: idx 0: (set of taints on) base, 1: retVal, 2+: parameters 240 | if (!calleeSummary.containsKey(Taint.getEmptyTaint())) { 241 | changed = true; 242 | List> emptyTaintSummary = new ArrayList<>(); 243 | for (int i = 0; i < callee.getParameterCount() + 2; i++) { 244 | emptyTaintSummary.add(new HashSet<>()); 245 | } 246 | calleeSummary.put(Taint.getEmptyTaint(), emptyTaintSummary); 247 | } 248 | 249 | // Initialize the summary for this invocation by elements copied from the empty taint summary 250 | List> summary = new ArrayList<>(); 251 | for (Set taints : calleeSummary.get(Taint.getEmptyTaint())) { 252 | Set newTaints = new HashSet<>(); 253 | newTaints.addAll(taints); 254 | summary.add(newTaints); 255 | } 256 | 257 | // Compute KILL and gather summary info for this invocation 258 | for (Taint t : in) { 259 | // Process base object 260 | if (base != null && t.taints(base)) { 261 | killSet.add(t); 262 | genCalleeEntryTaints(t, calleeThisLocal, stmt, calleeSummary, calleeTaintCache, summary, callee); 263 | } 264 | 265 | // Process parameters 266 | for (int i = 0; i < invoke.getArgCount(); i++) { 267 | Value arg = invoke.getArg(i); 268 | if (t.taints(arg)) { 269 | // Check if the param is basic type (we should pass on the taint in that case) 270 | if (!(arg.getType() instanceof PrimType)) { 271 | killSet.add(t); 272 | } 273 | Local calleeParam = calleeBody.getParameterLocal(i); 274 | genCalleeEntryTaints(t, calleeParam, stmt, calleeSummary, calleeTaintCache, summary, callee); 275 | } 276 | } 277 | } 278 | 279 | // Compute GEN from the gathered summary info 280 | // Process base object 281 | if (base != null) { 282 | Set baseTaints = summary.get(0); 283 | genSet.addAll(getTaintsFromInvokeSummary(baseTaints, base, stmt)); 284 | } 285 | 286 | // Process return value 287 | if (retVal != null) { 288 | Set retTaints = summary.get(1); 289 | genSet.addAll(getTaintsFromInvokeSummary(retTaints, retVal, stmt)); 290 | } 291 | 292 | // Process parameters 293 | for (int i = 0; i < invoke.getArgCount(); i++) { 294 | Value arg = invoke.getArg(i); 295 | Set argTaints = summary.get(2 + i); 296 | genSet.addAll(getTaintsFromInvokeSummary(argTaints, arg, stmt)); 297 | } 298 | } 299 | 300 | // KILL the INTERSECTION of all kill sets 301 | Set killSet = new HashSet<>(); 302 | for (int i = 0; i < killSets.size(); i++) { 303 | if (i == 0) { 304 | killSet.addAll(killSets.get(0)); 305 | } else { 306 | killSet.retainAll(killSets.get(i)); 307 | } 308 | } 309 | for (Taint t : killSet) { 310 | out.remove(t); 311 | } 312 | 313 | // GEN the UNION of all gen sets 314 | Set genSet = new HashSet<>(); 315 | for (Set s : genSets) { 316 | genSet.addAll(s); 317 | } 318 | for (Taint t : genSet) { 319 | out.add(t); 320 | } 321 | } 322 | 323 | private void genCalleeEntryTaints(Taint t, Value calleeVal, Stmt stmt, 324 | Map>> calleeSummary, 325 | Map calleeTaintCache, 326 | List> summary, 327 | SootMethod callee) { 328 | // Generate caller taint at call site 329 | Taint callerTaint = Taint.getTransferredTaintFor( 330 | t, t.getPlainValue(), stmt, method, currTaintCache, Taint.TransferType.Call); 331 | 332 | // Send caller taint to callee 333 | PhantomIdentityStmt phantomIdentityStmt = PhantomIdentityStmt.getInstance(callee); 334 | Taint calleeTaint = Taint.getTransferredTaintFor( 335 | callerTaint, calleeVal, phantomIdentityStmt, callee, calleeTaintCache); 336 | 337 | // Receive callee taint summary for the sent caller taint 338 | if (calleeSummary.containsKey(calleeTaint)) { 339 | List> lst = calleeSummary.get(calleeTaint); 340 | for (int i = 0; i < lst.size(); i++) { 341 | summary.get(i).addAll(lst.get(i)); 342 | } 343 | } else { 344 | // Generate new summary entry for the callee taint 345 | changed = true; 346 | List> newSummary = new ArrayList<>(); 347 | for (int i = 0; i < callee.getParameterCount() + 2; i++) { 348 | newSummary.add(new HashSet<>()); 349 | } 350 | calleeSummary.put(calleeTaint, newSummary); 351 | } 352 | } 353 | 354 | private Set getTaintsFromInvokeSummary(Set taints, Value callerVal, Stmt stmt) { 355 | Set out = new HashSet<>(); 356 | if (callerVal instanceof NullConstant) { 357 | return out; 358 | } 359 | for (Taint t : taints) { 360 | Taint callerTaint = Taint.getTransferredTaintFor( 361 | t, callerVal, stmt, method, currTaintCache, Taint.TransferType.Return); 362 | out.add(callerTaint); 363 | } 364 | return out; 365 | } 366 | 367 | private void visitReturn(Set in, Stmt stmt) { 368 | // Get the local representing @this (if exists) 369 | Local thiz = null; 370 | if (!body.getMethod().isStatic()) { 371 | thiz = body.getThisLocal(); 372 | } 373 | 374 | // Get return value (if exists) 375 | Value retVal = null; 376 | if (stmt instanceof ReturnStmt) { 377 | retVal = ((ReturnStmt) stmt).getOp(); 378 | } 379 | 380 | // Get the list of Locals representing the parameters (on LHS of IdentityStmt) 381 | List paramLocals = body.getParameterLocals(); 382 | 383 | List> summary = currMethodSummary.get(entryTaint); 384 | for (Taint t : in) { 385 | // Check if t taints base object 386 | if (thiz != null && t.taints(thiz)) { 387 | Taint newTaint = Taint.getTransferredTaintFor( 388 | t, t.getPlainValue(), phantomRetStmt, method, currTaintCache); 389 | changed |= summary.get(0).add(newTaint); 390 | } 391 | 392 | // Check if t taints return value 393 | if (retVal != null && t.taints(retVal)) { 394 | Taint newTaint = Taint.getTransferredTaintFor( 395 | t, t.getPlainValue(), phantomRetStmt, method, currTaintCache); 396 | changed |= summary.get(1).add(newTaint); 397 | } 398 | 399 | // Check if t taints object-type parameters 400 | for (int i = 0; i < paramLocals.size(); i++) { 401 | Local paramLocal = paramLocals.get(i); 402 | // Check if the param is basic type (we should not taint them in that case) 403 | if (!(paramLocal.getType() instanceof PrimType) && t.taints(paramLocal)) { 404 | Taint newTaint = Taint.getTransferredTaintFor( 405 | t, t.getPlainValue(), phantomRetStmt, method, currTaintCache); 406 | changed |= summary.get(2 + i).add(newTaint); 407 | } 408 | } 409 | } 410 | } 411 | 412 | private void visitSink(Set in, Stmt stmt) { 413 | if (!stmt.containsInvokeExpr()) return; 414 | InvokeExpr invoke = stmt.getInvokeExpr(); 415 | 416 | Value base = null; 417 | if (invoke instanceof InstanceInvokeExpr) { 418 | base = ((InstanceInvokeExpr) invoke).getBase(); 419 | } 420 | 421 | for (Taint t : in) { 422 | // Process base object 423 | if (base != null && t.taints(base)) { 424 | Taint sinkTaint = Taint.getTransferredTaintFor( 425 | t, t.getPlainValue(), stmt, method, currTaintCache); 426 | sinkTaint.setSink(); 427 | sinks.add(sinkTaint); 428 | } 429 | 430 | // Process parameters 431 | for (int i = 0; i < invoke.getArgCount(); i++) { 432 | Value arg = invoke.getArg(i); 433 | if (t.taints(arg)) { 434 | Taint sinkTaint = Taint.getTransferredTaintFor( 435 | t, t.getPlainValue(), stmt, method, currTaintCache); 436 | sinkTaint.setSink(); 437 | sinks.add(sinkTaint); 438 | } 439 | } 440 | } 441 | } 442 | 443 | @Override 444 | protected Set newInitialFlow() { 445 | return new HashSet<>(); 446 | } 447 | 448 | @Override 449 | protected Set entryInitialFlow() { 450 | Set entryTaints = new HashSet<>(); 451 | if (!entryTaint.isEmpty()) { 452 | entryTaints.add(entryTaint); 453 | } 454 | return entryTaints; 455 | } 456 | 457 | @Override 458 | protected void merge(Set in1, Set in2, Set out) { 459 | out.clear(); 460 | out.addAll(in1); 461 | out.addAll(in2); 462 | } 463 | 464 | @Override 465 | protected void copy(Set source, Set dest) { 466 | dest.clear(); 467 | dest.addAll(source); 468 | } 469 | 470 | } 471 | -------------------------------------------------------------------------------- /src/main/java/taintAnalysis/sourceSinkManager/ISourceSinkManager.java: -------------------------------------------------------------------------------- 1 | package taintAnalysis.sourceSinkManager; 2 | 3 | import soot.jimple.Stmt; 4 | 5 | public interface ISourceSinkManager { 6 | 7 | /** 8 | * Checks whether the given statement is a source statement 9 | * 10 | * @param stmt The statement to check 11 | * @return True if the given statement is a source statement, otherwise false 12 | */ 13 | boolean isSource(Stmt stmt); 14 | 15 | /** 16 | * Checks whether the given statement is a sink statement 17 | * 18 | * @param stmt The statement to check 19 | * @return True if the given statement is a sink statement, otherwise false 20 | */ 21 | boolean isSink(Stmt stmt); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/taintAnalysis/sourceSinkManager/SourceSinkManager.java: -------------------------------------------------------------------------------- 1 | package taintAnalysis.sourceSinkManager; 2 | 3 | import configInterface.ConfigInterface; 4 | import soot.*; 5 | import soot.jimple.*; 6 | 7 | public class SourceSinkManager implements ISourceSinkManager { 8 | 9 | private ConfigInterface interfaces; 10 | 11 | public SourceSinkManager(ConfigInterface interfaces) { 12 | this.interfaces = interfaces; 13 | } 14 | 15 | @Override 16 | public boolean isSource(Stmt stmt) { 17 | // We only support method calls 18 | if (!stmt.containsInvokeExpr()) 19 | return false; 20 | 21 | // Check for predefined getter patterns 22 | InvokeExpr expr = stmt.getInvokeExpr(); 23 | if (interfaces.isGetter(expr)) 24 | return true; 25 | 26 | // nothing found 27 | return false; 28 | } 29 | 30 | @Override 31 | public boolean isSink(Stmt stmt) { 32 | // external lib calls considered as sink 33 | if (stmt.containsInvokeExpr()) { 34 | InvokeExpr expr = stmt.getInvokeExpr(); 35 | 36 | // ignore logger calls 37 | if (isLogger(expr)) { 38 | return false; 39 | } 40 | 41 | // ignore certain java lib calls 42 | if (isExcludedJavaLangCall(expr)) { 43 | return false; 44 | } 45 | 46 | SootMethod callee = expr.getMethod(); 47 | if (!callee.getDeclaringClass().isApplicationClass()) { 48 | return true; 49 | } 50 | } 51 | 52 | // nothing found 53 | return false; 54 | } 55 | 56 | protected boolean isLogger(InvokeExpr iexpr) { 57 | if (iexpr instanceof InstanceInvokeExpr) { 58 | Value base = ((InstanceInvokeExpr) iexpr).getBase(); 59 | if (base.getType() instanceof RefType) { 60 | RefType rty = (RefType) base.getType(); 61 | if (rty.getClassName().contains("Logger")) { 62 | return true; 63 | } 64 | } 65 | } 66 | return false; 67 | } 68 | 69 | protected boolean isExcludedJavaLangCall(InvokeExpr iexpr) { 70 | SootClass sootClass = iexpr.getMethod().getDeclaringClass(); 71 | String className = sootClass.getName(); 72 | String packageName = sootClass.getPackageName(); 73 | if (!packageName.startsWith("java")) { 74 | return false; 75 | } 76 | if (className.equals("java.io.PrintStream") || 77 | className.equals("java.lang.Boolean") || 78 | className.equals("java.lang.Byte") || 79 | className.equals("java.lang.Character") || 80 | className.equals("java.lang.Class") || 81 | className.equals("java.lang.Double") || 82 | className.equals("java.lang.Float") || 83 | className.equals("java.lang.Integer") || 84 | className.equals("java.lang.Long") || 85 | className.equals("java.lang.Math") || 86 | className.equals("java.lang.Number") || 87 | className.equals("java.lang.Object") || 88 | className.equals("java.lang.String") || 89 | className.equals("java.lang.StringBuffer") || 90 | className.equals("java.lang.StringBuilder") || 91 | packageName.equals("java.lang.ref") || 92 | packageName.equals("java.lang.reflect") || 93 | packageName.equals("java.math") || 94 | packageName.equals("java.util")) { 95 | return true; 96 | } 97 | return false; 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/taintAnalysis/taintWrapper/ITaintWrapper.java: -------------------------------------------------------------------------------- 1 | package taintAnalysis.taintWrapper; 2 | 3 | import soot.SootMethod; 4 | import soot.jimple.Stmt; 5 | import taintAnalysis.Taint; 6 | 7 | import java.util.Map; 8 | import java.util.Set; 9 | 10 | 11 | public interface ITaintWrapper { 12 | 13 | /** 14 | * Checks an invocation statement for black-box taint propagation. This allows 15 | * the wrapper to artificially propagate taints over method invocations without 16 | * requiring the analysis to look inside the method. 17 | * @param in The in-set of taints before the invocation statement 18 | * @param stmt The invocation statement which to check for black-box taint propagation 19 | * @param caller The caller method of the invocation statement 20 | * @param killSet The kill sets of the invocation statement computed with the provided in-set 21 | * @param genSet The gen sets of the invocation statement computed with the provided in-set 22 | * @param taintCache The taint cache of the caller method, 23 | * used to ensure global uniqueness of generated taint objects 24 | */ 25 | void genTaintsForMethodInternal(Set in, Stmt stmt, SootMethod caller, 26 | Set killSet, Set genSet, 27 | Map taintCache); 28 | 29 | /** 30 | * Checks whether this taint wrapper can in general produce artificial taints 31 | * for the given callee. If an implementation returns "false" for a callee, 32 | * all call sites for this callee might be removed if not needed elsewhere. 33 | * @param method The method to check 34 | * @return True if this taint wrapper can in general produce taints for the 35 | * given method. 36 | */ 37 | boolean supportsCallee(SootMethod method); 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/taintAnalysis/taintWrapper/TaintWrapper.java: -------------------------------------------------------------------------------- 1 | package taintAnalysis.taintWrapper; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.FileReader; 6 | import java.io.IOException; 7 | import java.io.Reader; 8 | import java.util.HashSet; 9 | import java.util.Map; 10 | import java.util.Set; 11 | 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | import soot.SootMethod; 16 | import soot.Value; 17 | import soot.jimple.AssignStmt; 18 | import soot.jimple.InstanceInvokeExpr; 19 | import soot.jimple.InvokeExpr; 20 | import soot.jimple.Stmt; 21 | import taintAnalysis.Taint; 22 | 23 | import static assertion.Assert.assertNotNull; 24 | import static assertion.Assert.assertTrue; 25 | 26 | 27 | /** 28 | * Five lists of methods are passed which contain signatures of instance methods 29 | * with different tainting behavior. 30 | * 31 | * 1. TaintBoth: taint both the base object and the return value 32 | * if the base object is tainted or one of the parameter is tainted 33 | * 2. TaintReturn: taint only the return value 34 | * if the base object is tainted or one of the parameter is tainted 35 | * 3. TaintBase: taint only the base object 36 | * if the base object is tainted or one of the parameter is tainted 37 | * 4. KillTaint: kill the taint if the base object is tainted 38 | * 5. Exclude: excluded, do nothing 39 | */ 40 | public class TaintWrapper implements ITaintWrapper { 41 | 42 | private final Logger logger = LoggerFactory.getLogger(getClass()); 43 | 44 | private final Set taintBothList; 45 | private final Set taintReturnList; 46 | private final Set taintBaseList; 47 | private final Set excludeList; 48 | private final Set killList; 49 | 50 | /** 51 | * The possible effects this taint wrapper can have on a method invocation 52 | */ 53 | private enum MethodWrapType { 54 | /** 55 | * This method can create a new taint for both the base object and the return value 56 | */ 57 | TaintBoth, 58 | /** 59 | * This method can create a new taint only for the return value 60 | */ 61 | TaintReturn, 62 | /** 63 | * This method can create a new taint only for the base object 64 | */ 65 | TaintBase, 66 | /** 67 | * This method can kill a taint 68 | */ 69 | KillTaint, 70 | /** 71 | * This method has not been named in the taint wrapper configuration 72 | */ 73 | Exclude, 74 | /** 75 | * This method has not been named in the taint wrapper configuration 76 | */ 77 | NotRegistered 78 | } 79 | 80 | public TaintWrapper(Set taintBothList, Set taintReturnList, Set taintBaseList, 81 | Set excludeList, Set killList) { 82 | this.taintBothList = taintBothList; 83 | this.taintReturnList = taintReturnList; 84 | this.taintBaseList = taintBaseList; 85 | this.excludeList = excludeList; 86 | this.killList = killList; 87 | } 88 | 89 | public static TaintWrapper getDefault() throws IOException { 90 | return new TaintWrapper("TaintWrapperSource.txt"); 91 | } 92 | 93 | public TaintWrapper(String f) throws IOException { 94 | Reader reader = new FileReader(new File(f).getAbsoluteFile()); 95 | BufferedReader bufReader = new BufferedReader(reader); 96 | try { 97 | String line = bufReader.readLine(); 98 | this.taintBothList = new HashSet<>(); 99 | this.taintReturnList = new HashSet<>(); 100 | this.taintBaseList = new HashSet<>(); 101 | this.excludeList = new HashSet<>(); 102 | this.killList = new HashSet<>(); 103 | while (line != null) { 104 | if (!line.isEmpty() && !line.startsWith("%")) { 105 | if (line.startsWith("~")) 106 | excludeList.add(line.substring(1)); 107 | else if (line.startsWith("-")) 108 | killList.add(line.substring(1)); 109 | else if (line.startsWith("r")) 110 | taintReturnList.add(line.substring(1)); 111 | else if (line.startsWith("b")) 112 | taintBaseList.add(line.substring(1)); 113 | else 114 | taintBothList.add(line); 115 | } 116 | line = bufReader.readLine(); 117 | } 118 | logger.info("Loaded wrapper entries for {} taint-both methods, {} taint-return methods, " + 119 | "{} taint-base methods, {} kill-taint methods, and {} exclusions", 120 | taintBothList.size(), taintReturnList.size(), taintBaseList.size(), 121 | excludeList.size(), killList.size()); 122 | } finally { 123 | bufReader.close(); 124 | } 125 | } 126 | 127 | public TaintWrapper(TaintWrapper taintWrapper) { 128 | this(taintWrapper.taintBothList, taintWrapper.taintReturnList, taintWrapper.taintBaseList, 129 | taintWrapper.excludeList, taintWrapper.killList); 130 | } 131 | 132 | @Override 133 | public void genTaintsForMethodInternal(Set in, Stmt stmt, SootMethod caller, 134 | Set killSet, Set genSet, 135 | Map taintCache) { 136 | assertTrue(stmt.containsInvokeExpr()); 137 | InvokeExpr invoke = stmt.getInvokeExpr(); 138 | SootMethod callee = invoke.getMethod(); 139 | assertNotNull(callee); 140 | 141 | // Do not provide models for application classes 142 | if (callee.getDeclaringClass().isApplicationClass()) 143 | return; 144 | 145 | MethodWrapType wrapType = getMethodWrapType(callee); 146 | if (wrapType == MethodWrapType.Exclude) 147 | return; 148 | 149 | // Get the base object of this invocation in caller and the corresponding this object in callee (if exists) 150 | Value base = null; 151 | if (invoke instanceof InstanceInvokeExpr) { 152 | base = ((InstanceInvokeExpr) invoke).getBase(); 153 | } 154 | 155 | // Get the retVal of this invocation in caller (if applies) 156 | Value retVal = null; 157 | if (stmt instanceof AssignStmt) { 158 | retVal = ((AssignStmt) stmt).getLeftOp(); 159 | } 160 | 161 | for (Taint t : in) { 162 | boolean baseTainted = false; 163 | boolean paramTainted = false; 164 | 165 | // Process base object 166 | if (base != null && t.taints(base)) { 167 | if (wrapType == MethodWrapType.KillTaint) { 168 | killSet.add(t); 169 | continue; 170 | } 171 | baseTainted = true; 172 | } 173 | 174 | // Process parameters 175 | for (int i = 0; i < invoke.getArgCount(); i++) { 176 | Value arg = invoke.getArg(i); 177 | if (t.taints(arg)) { 178 | paramTainted = true; 179 | } 180 | } 181 | 182 | if (baseTainted || paramTainted) { 183 | // Taint base 184 | if (base != null && 185 | (wrapType == MethodWrapType.TaintBoth || wrapType == MethodWrapType.TaintBase)) { 186 | if (t.taints(base)) { 187 | killSet.add(t); 188 | } 189 | Taint newTaint = Taint.getTaintFor(t, base, stmt, caller, taintCache); 190 | genSet.add(newTaint); 191 | } 192 | 193 | // Taint return val (Note that if base is tainted, we also taint the ret val) 194 | if (retVal != null && (baseTainted || 195 | wrapType == MethodWrapType.TaintBoth || 196 | wrapType == MethodWrapType.TaintReturn)) { 197 | Taint newTaint = Taint.getTaintFor(t, retVal, stmt, caller, taintCache); 198 | genSet.add(newTaint); 199 | } 200 | } 201 | } 202 | } 203 | 204 | @Override 205 | public boolean supportsCallee(SootMethod method) { 206 | if (!method.getDeclaringClass().isApplicationClass()) 207 | return true; 208 | return false; 209 | } 210 | 211 | /** 212 | * Gets the type of action the taint wrapper shall perform on a given method 213 | * 214 | * @param method The method to look for 215 | * @return The type of action to be performed on the given method 216 | */ 217 | private MethodWrapType getMethodWrapType(SootMethod method) { 218 | String sig = method.getSignature(); 219 | if (taintBothList.contains(sig)) 220 | return MethodWrapType.TaintBoth; 221 | if (taintReturnList.contains(sig)) 222 | return MethodWrapType.TaintReturn; 223 | if (taintBaseList.contains(sig)) 224 | return MethodWrapType.TaintBase; 225 | if (excludeList.contains(sig)) 226 | return MethodWrapType.Exclude; 227 | if (killList.contains(sig)) 228 | return MethodWrapType.KillTaint; 229 | return MethodWrapType.NotRegistered; 230 | } 231 | 232 | } 233 | -------------------------------------------------------------------------------- /src/main/java/taintAnalysis/utility/PhantomIdentityStmt.java: -------------------------------------------------------------------------------- 1 | package taintAnalysis.utility; 2 | 3 | import soot.*; 4 | import soot.jimple.ArrayRef; 5 | import soot.jimple.FieldRef; 6 | import soot.jimple.InvokeExpr; 7 | import soot.jimple.Stmt; 8 | import soot.tagkit.Host; 9 | import soot.tagkit.Tag; 10 | import soot.util.Switch; 11 | 12 | import java.util.List; 13 | import java.util.Objects; 14 | 15 | public class PhantomIdentityStmt implements Stmt { 16 | 17 | private final SootMethod method; 18 | 19 | public static PhantomIdentityStmt getInstance(SootMethod method) { 20 | return new PhantomIdentityStmt(method); 21 | } 22 | 23 | private PhantomIdentityStmt(SootMethod method) { 24 | this.method = method; 25 | } 26 | 27 | @Override 28 | public String toString() { 29 | return "identity_stmt"; 30 | } 31 | 32 | @Override 33 | public boolean equals(Object o) { 34 | if (this == o) return true; 35 | if (o == null || getClass() != o.getClass()) return false; 36 | PhantomIdentityStmt that = (PhantomIdentityStmt) o; 37 | return Objects.equals(method, that.method); 38 | } 39 | 40 | @Override 41 | public int hashCode() { 42 | return Objects.hash(method); 43 | } 44 | 45 | @Override 46 | public List getUseBoxes() { 47 | return null; 48 | } 49 | 50 | @Override 51 | public List getDefBoxes() { 52 | return null; 53 | } 54 | 55 | @Override 56 | public List getUnitBoxes() { 57 | return null; 58 | } 59 | 60 | @Override 61 | public List getBoxesPointingToThis() { 62 | return null; 63 | } 64 | 65 | @Override 66 | public void addBoxPointingToThis(UnitBox b) { 67 | 68 | } 69 | 70 | @Override 71 | public void removeBoxPointingToThis(UnitBox b) { 72 | 73 | } 74 | 75 | @Override 76 | public void clearUnitBoxes() { 77 | 78 | } 79 | 80 | @Override 81 | public List getUseAndDefBoxes() { 82 | return null; 83 | } 84 | 85 | @Override 86 | public Object clone() { 87 | return null; 88 | } 89 | 90 | @Override 91 | public boolean fallsThrough() { 92 | return false; 93 | } 94 | 95 | @Override 96 | public boolean branches() { 97 | return false; 98 | } 99 | 100 | @Override 101 | public void toString(UnitPrinter up) { 102 | 103 | } 104 | 105 | @Override 106 | public void redirectJumpsToThisTo(Unit newLocation) { 107 | 108 | } 109 | 110 | @Override 111 | public boolean containsInvokeExpr() { 112 | return false; 113 | } 114 | 115 | @Override 116 | public InvokeExpr getInvokeExpr() { 117 | return null; 118 | } 119 | 120 | @Override 121 | public ValueBox getInvokeExprBox() { 122 | return null; 123 | } 124 | 125 | @Override 126 | public boolean containsArrayRef() { 127 | return false; 128 | } 129 | 130 | @Override 131 | public ArrayRef getArrayRef() { 132 | return null; 133 | } 134 | 135 | @Override 136 | public ValueBox getArrayRefBox() { 137 | return null; 138 | } 139 | 140 | @Override 141 | public boolean containsFieldRef() { 142 | return false; 143 | } 144 | 145 | @Override 146 | public FieldRef getFieldRef() { 147 | return null; 148 | } 149 | 150 | @Override 151 | public ValueBox getFieldRefBox() { 152 | return null; 153 | } 154 | 155 | @Override 156 | public List getTags() { 157 | return null; 158 | } 159 | 160 | @Override 161 | public Tag getTag(String aName) { 162 | return null; 163 | } 164 | 165 | @Override 166 | public void addTag(Tag t) { 167 | 168 | } 169 | 170 | @Override 171 | public void removeTag(String name) { 172 | 173 | } 174 | 175 | @Override 176 | public boolean hasTag(String aName) { 177 | return false; 178 | } 179 | 180 | @Override 181 | public void removeAllTags() { 182 | 183 | } 184 | 185 | @Override 186 | public void addAllTagsOf(Host h) { 187 | 188 | } 189 | 190 | @Override 191 | public int getJavaSourceStartLineNumber() { 192 | return 0; 193 | } 194 | 195 | @Override 196 | public int getJavaSourceStartColumnNumber() { 197 | return 0; 198 | } 199 | 200 | @Override 201 | public void apply(Switch sw) { 202 | 203 | } 204 | 205 | } 206 | -------------------------------------------------------------------------------- /src/main/java/taintAnalysis/utility/PhantomRetStmt.java: -------------------------------------------------------------------------------- 1 | package taintAnalysis.utility; 2 | 3 | import soot.*; 4 | import soot.jimple.ArrayRef; 5 | import soot.jimple.FieldRef; 6 | import soot.jimple.InvokeExpr; 7 | import soot.jimple.Stmt; 8 | import soot.tagkit.Host; 9 | import soot.tagkit.Tag; 10 | import soot.util.Switch; 11 | 12 | import java.util.List; 13 | import java.util.Objects; 14 | 15 | public class PhantomRetStmt implements Stmt { 16 | 17 | private final SootMethod method; 18 | 19 | public static PhantomRetStmt getInstance(SootMethod method) { 20 | return new PhantomRetStmt(method); 21 | } 22 | 23 | private PhantomRetStmt(SootMethod method) { 24 | this.method = method; 25 | } 26 | 27 | @Override 28 | public String toString() { 29 | return "return"; 30 | } 31 | 32 | @Override 33 | public boolean equals(Object o) { 34 | if (this == o) return true; 35 | if (o == null || getClass() != o.getClass()) return false; 36 | PhantomRetStmt that = (PhantomRetStmt) o; 37 | return Objects.equals(method, that.method); 38 | } 39 | 40 | @Override 41 | public int hashCode() { 42 | return Objects.hash(method); 43 | } 44 | 45 | @Override 46 | public List getUseBoxes() { 47 | return null; 48 | } 49 | 50 | @Override 51 | public List getDefBoxes() { 52 | return null; 53 | } 54 | 55 | @Override 56 | public List getUnitBoxes() { 57 | return null; 58 | } 59 | 60 | @Override 61 | public List getBoxesPointingToThis() { 62 | return null; 63 | } 64 | 65 | @Override 66 | public void addBoxPointingToThis(UnitBox b) { 67 | 68 | } 69 | 70 | @Override 71 | public void removeBoxPointingToThis(UnitBox b) { 72 | 73 | } 74 | 75 | @Override 76 | public void clearUnitBoxes() { 77 | 78 | } 79 | 80 | @Override 81 | public List getUseAndDefBoxes() { 82 | return null; 83 | } 84 | 85 | @Override 86 | public Object clone() { 87 | return null; 88 | } 89 | 90 | @Override 91 | public boolean fallsThrough() { 92 | return false; 93 | } 94 | 95 | @Override 96 | public boolean branches() { 97 | return false; 98 | } 99 | 100 | @Override 101 | public void toString(UnitPrinter up) { 102 | 103 | } 104 | 105 | @Override 106 | public void redirectJumpsToThisTo(Unit newLocation) { 107 | 108 | } 109 | 110 | @Override 111 | public boolean containsInvokeExpr() { 112 | return false; 113 | } 114 | 115 | @Override 116 | public InvokeExpr getInvokeExpr() { 117 | return null; 118 | } 119 | 120 | @Override 121 | public ValueBox getInvokeExprBox() { 122 | return null; 123 | } 124 | 125 | @Override 126 | public boolean containsArrayRef() { 127 | return false; 128 | } 129 | 130 | @Override 131 | public ArrayRef getArrayRef() { 132 | return null; 133 | } 134 | 135 | @Override 136 | public ValueBox getArrayRefBox() { 137 | return null; 138 | } 139 | 140 | @Override 141 | public boolean containsFieldRef() { 142 | return false; 143 | } 144 | 145 | @Override 146 | public FieldRef getFieldRef() { 147 | return null; 148 | } 149 | 150 | @Override 151 | public ValueBox getFieldRefBox() { 152 | return null; 153 | } 154 | 155 | @Override 156 | public List getTags() { 157 | return null; 158 | } 159 | 160 | @Override 161 | public Tag getTag(String aName) { 162 | return null; 163 | } 164 | 165 | @Override 166 | public void addTag(Tag t) { 167 | 168 | } 169 | 170 | @Override 171 | public void removeTag(String name) { 172 | 173 | } 174 | 175 | @Override 176 | public boolean hasTag(String aName) { 177 | return false; 178 | } 179 | 180 | @Override 181 | public void removeAllTags() { 182 | 183 | } 184 | 185 | @Override 186 | public void addAllTagsOf(Host h) { 187 | 188 | } 189 | 190 | @Override 191 | public int getJavaSourceStartLineNumber() { 192 | return 0; 193 | } 194 | 195 | @Override 196 | public int getJavaSourceStartColumnNumber() { 197 | return 0; 198 | } 199 | 200 | @Override 201 | public void apply(Switch sw) { 202 | 203 | } 204 | 205 | } 206 | -------------------------------------------------------------------------------- /src/main/java/utility/Config.java: -------------------------------------------------------------------------------- 1 | package utility; 2 | 3 | import assertion.Assert; 4 | import configInterface.ConfigInterface; 5 | import configInterface.HadoopInterface; 6 | import configInterface.SparkInterface; 7 | import configInterface.TestInterface; 8 | 9 | import java.io.File; 10 | import java.util.ArrayList; 11 | import java.util.LinkedList; 12 | import java.util.List; 13 | 14 | public class Config { 15 | 16 | public static final String TEST_PATH = "Test/out/artifacts/Test_jar/Test.jar"; 17 | public static final String HADOOP_PATH = "app/hadoop-3.3.0/"; 18 | public static final String HBASE_PATH = "app/hbase-2.3.1/"; 19 | public static final String ALLUXIO_PATH = "app/alluxio-1.8.0/"; 20 | public static final String ZOOKEEPER_PATH = "app/apache-zookeeper-3.5.6-bin/"; 21 | public static final String SPARK_PATH = "app/spark-2.4.6-bin-hadoop2.7/"; 22 | public static final String[] SUPPORTED = {"test","hdfs","mapreduce","yarn","hadoop_common","hadoop_tools", 23 | "hbase","alluxio","zookeeper","spark"}; 24 | 25 | public static final String[][] CONFIGS = { 26 | { "test", 27 | "", 28 | "", 29 | }, 30 | { 31 | "hadoop", 32 | "hdfs", 33 | HADOOP_PATH + "share/hadoop/hdfs/", 34 | }, 35 | { 36 | "hadoop", 37 | "mapreduce", 38 | HADOOP_PATH + "share/hadoop/mapreduce/" 39 | }, 40 | { 41 | "hadoop", 42 | "yarn", 43 | HADOOP_PATH + "share/hadoop/yarn/" 44 | }, 45 | { 46 | "hadoop", 47 | "common", 48 | HADOOP_PATH + "share/hadoop/common/" 49 | }, 50 | 51 | { 52 | "hadoop", 53 | "tools", 54 | HADOOP_PATH + "share/hadoop/tools/lib/" 55 | }, 56 | { 57 | "hbase", 58 | "", 59 | HBASE_PATH + "lib/" 60 | }, 61 | { 62 | "alluxio", 63 | "", 64 | ALLUXIO_PATH + "lib/" 65 | }, 66 | { 67 | "zookeeper", 68 | "", 69 | ZOOKEEPER_PATH + "lib/" 70 | }, 71 | { 72 | "spark", 73 | "", 74 | SPARK_PATH + "jars/" 75 | }, 76 | }; 77 | 78 | public static String[] getCfg(String app) throws IllegalArgumentException { 79 | System.out.println(app); 80 | int i = 0; 81 | for (; i < SUPPORTED.length; i++) { 82 | if (SUPPORTED[i].equals(app)) { 83 | return CONFIGS[i]; 84 | } 85 | } 86 | if (i == SUPPORTED.length) { 87 | throw new IllegalArgumentException(app + " not found in supported application"); 88 | } 89 | return null; 90 | } 91 | 92 | public static ConfigInterface getInterface(String[] cfg){ 93 | // if (cfg[0].contains("alluxio")) { 94 | // return new AlluxioInterface(); 95 | // } 96 | // if (cfg[0].contains("zookeeper")) { 97 | // return new ZooKeeperInterface(); 98 | // } 99 | if (cfg[0].contains("test")) { 100 | return new TestInterface(); 101 | } 102 | if (cfg[0].contains("spark")) { 103 | return new SparkInterface(); 104 | } 105 | return new HadoopInterface(); 106 | } 107 | 108 | public static List getClassPaths(String[] cfg) { 109 | String clsRoot; 110 | List classPaths = new LinkedList<>(); 111 | 112 | if (cfg[0].compareTo("test") == 0) { 113 | classPaths.add(Config.TEST_PATH); 114 | } 115 | 116 | if (cfg[0].compareTo("hadoop") == 0) { 117 | clsRoot = Config.HADOOP_PATH; 118 | if (cfg[1].contains("hdfs")) { 119 | classPaths.addAll(getSootClassPaths(clsRoot + "share/hadoop/hdfs/lib/")); 120 | classPaths.addAll(getSootClassPaths(clsRoot + "share/hadoop/hdfs/")); 121 | } else if (cfg[1].contains("yarn")) { 122 | classPaths.addAll(getSootClassPaths(clsRoot + "share/hadoop/yarn/lib/")); 123 | classPaths.addAll(getSootClassPaths(clsRoot + "share/hadoop/yarn/")); 124 | } else if (cfg[1].contains("mapreduce")) { 125 | classPaths.addAll(getSootClassPaths(clsRoot + "share/hadoop/mapreduce/")); 126 | } else if (cfg[1].contains("tools")) { 127 | classPaths.addAll(getSootClassPaths(clsRoot + "share/hadoop/tools/lib/")); 128 | } else if (cfg[1].contains("common")) { 129 | 130 | } else { 131 | Assert.assertImpossible("UNRECOGNIZED COMPONENT: " + cfg[1]); 132 | } 133 | 134 | classPaths.addAll(getSootClassPaths(clsRoot + "share/hadoop/common/lib/")); 135 | classPaths.addAll(getSootClassPaths(clsRoot + "share/hadoop/common/")); 136 | } 137 | 138 | if (cfg[0].compareTo("hbase") == 0) { 139 | clsRoot = Config.HBASE_PATH; 140 | classPaths.addAll(getSootClassPaths(clsRoot + "lib/")); 141 | } 142 | 143 | if (cfg[0].compareTo("alluxio") == 0) { 144 | clsRoot = Config.ALLUXIO_PATH; 145 | classPaths.addAll(getSootClassPaths(clsRoot + "lib/")); 146 | } 147 | 148 | if (cfg[0].compareTo("zookeeper") == 0){ 149 | clsRoot = Config.ZOOKEEPER_PATH; 150 | classPaths.addAll(getSootClassPaths(clsRoot + "lib/")); 151 | } 152 | 153 | if (cfg[0].compareTo("spark") == 0){ 154 | clsRoot = Config.SPARK_PATH; 155 | classPaths.addAll(getSootClassPaths(clsRoot + "jars/")); 156 | } 157 | 158 | return classPaths; 159 | } 160 | 161 | public static List getSourcePaths(String[] cfg) { 162 | if (cfg[0].compareTo("test") == 0) { 163 | List sourcePaths = new ArrayList<>(); 164 | sourcePaths.add(Config.TEST_PATH); 165 | return sourcePaths; 166 | } 167 | 168 | if (cfg[0].compareTo("hadoop") == 0) { 169 | List sourcePaths = new ArrayList<>(); 170 | 171 | // include Hadoop Common for all Hadoop subcomponents 172 | String[] hadoopCommonCfg = CONFIGS[4]; 173 | sourcePaths.addAll(getJars(hadoopCommonCfg[2])); 174 | 175 | if (cfg[1].contains("tools")) { 176 | sourcePaths.addAll(getJarsHadoopTools(cfg[2])); 177 | } else if (!cfg[1].contains("common")){ 178 | sourcePaths.addAll(getJars(cfg[2])); 179 | } 180 | 181 | return sourcePaths; 182 | } 183 | 184 | if (cfg[0].compareTo("hbase") == 0) { 185 | return getJarsHbase(cfg[2]); 186 | } 187 | 188 | if (cfg[0].compareTo("alluxio") == 0) { 189 | return getJars(cfg[2]); 190 | } 191 | 192 | if (cfg[0].compareTo("zookeeper") == 0) { 193 | return getJars(cfg[2]); 194 | } 195 | 196 | if (cfg[0].compareTo("spark") == 0) { 197 | return getJarsSpark(cfg[2]); 198 | } 199 | 200 | return null; 201 | } 202 | 203 | public static List getJarsHadoopTools(String dir) { 204 | List jars = new LinkedList(); 205 | File sdir = new File(dir); 206 | Assert.assertTrue(sdir.isDirectory()); 207 | 208 | for (File f : sdir.listFiles()) { 209 | if (f.getName().endsWith(".jar") && 210 | f.getName().startsWith("hadoop") && 211 | !f.getName().contains("test") && 212 | !f.getName().contains("example")) { 213 | jars.add(f.getAbsolutePath()); 214 | } 215 | } 216 | return jars; 217 | } 218 | 219 | public static List getJarsHbase(String dir) { 220 | List jars = new LinkedList(); 221 | File sdir = new File(dir); 222 | Assert.assertTrue(sdir.isDirectory()); 223 | 224 | for (File f : sdir.listFiles()) { 225 | if (f.getName().endsWith(".jar") && 226 | f.getName().startsWith("hbase") && 227 | !f.getName().contains("test") && 228 | !f.getName().contains("example")) { 229 | jars.add(f.getAbsolutePath()); 230 | } 231 | } 232 | return jars; 233 | } 234 | 235 | public static List getJarsSpark(String dir) { 236 | List jars = new LinkedList(); 237 | File sdir = new File(dir); 238 | Assert.assertTrue(sdir.isDirectory()); 239 | 240 | for (File f : sdir.listFiles()) { 241 | if (f.getName().endsWith(".jar") && 242 | f.getName().startsWith("spark") && 243 | !f.getName().contains("test") && 244 | !f.getName().contains("example")) { 245 | jars.add(f.getAbsolutePath()); 246 | } 247 | } 248 | return jars; 249 | } 250 | 251 | public static List getJars(String dir) { 252 | List jars = new LinkedList(); 253 | File sdir = new File(dir); 254 | Assert.assertTrue(sdir.isDirectory()); 255 | 256 | for (File f : sdir.listFiles()) { 257 | if (f.getName().endsWith(".jar") && 258 | !f.getName().contains("test") && 259 | !f.getName().contains("example")) { 260 | jars.add(f.getAbsolutePath()); 261 | } 262 | } 263 | return jars; 264 | } 265 | 266 | public static List getSootClassPaths(String dir) { 267 | List scp = new LinkedList<>(); 268 | File fdir = new File(dir); 269 | if (fdir.isDirectory() == false) { 270 | System.out.println("[FATAL] " + dir + " should be a directory path"); 271 | } 272 | for (File f : fdir.listFiles()) { 273 | if (f.getName().endsWith(".jar") && 274 | !f.getName().contains("test") && 275 | !f.getName().contains("example")) { 276 | scp.add(f.getAbsolutePath()); 277 | } 278 | } 279 | return scp; 280 | } 281 | 282 | } 283 | -------------------------------------------------------------------------------- /src/main/resources/simplelogger.properties: -------------------------------------------------------------------------------- 1 | org.slf4j.simpleLogger.defaultLogLevel=info -------------------------------------------------------------------------------- /src/test/java/taintAnalysisTest/HadoopInterAnalysisTest.java: -------------------------------------------------------------------------------- 1 | package taintAnalysisTest; 2 | 3 | public class HadoopInterAnalysisTest { 4 | } 5 | -------------------------------------------------------------------------------- /src/test/java/taintAnalysisTest/HadoopIntraAnalysisTest.java: -------------------------------------------------------------------------------- 1 | package taintAnalysisTest; 2 | 3 | import configInterface.ConfigInterface; 4 | import configInterface.HadoopInterface; 5 | import org.junit.Test; 6 | import org.junit.Assert; 7 | import soot.SootMethod; 8 | import soot.jimple.Stmt; 9 | import taintAnalysis.Taint; 10 | import taintAnalysis.TaintAnalysisDriver; 11 | import taintAnalysis.sourceSinkManager.ISourceSinkManager; 12 | import taintAnalysis.sourceSinkManager.SourceSinkManager; 13 | 14 | import java.util.*; 15 | 16 | public class HadoopIntraAnalysisTest extends TaintAnalysisTest { 17 | // 18 | // @Test 19 | // public void testIntraAnalysisHadoop() { 20 | // System.out.println("#### testing IntraAnalysis on Hadoop"); 21 | // String hadoopJar = "app/hadoop-3.3.0/share/hadoop/common/hadoop-common-3.3.0.jar"; 22 | // List srcPaths = new ArrayList<>(); 23 | // srcPaths.add(hadoopJar); 24 | // ConfigInterface configInterface = new HadoopInterface(); 25 | // ISourceSinkManager sourceSinkManager = new SourceSinkManager(configInterface); 26 | // TaintAnalysisDriver driver = new TaintAnalysisDriver(sourceSinkManager); 27 | // List> results = driver.runIntraTaintAnalysis(srcPaths, srcPaths).getSourceLists(); 28 | // for (List result : results) { 29 | //// System.out.println("> result"); 30 | // if (result.size() > 0) { 31 | // SootMethod method = result.get(0).getMethod(); 32 | // String methodString = method.toString(); 33 | // String declaringClass = method.getDeclaringClass().toString(); 34 | // Stmt stmt = result.get(0).getStmt(); 35 | // if (methodString.contains("setConf") && declaringClass.contains("ScriptBasedMapping$") ) { 36 | // System.out.println("##### ScriptBasedMapping object using setConf method"); 37 | // Set succ = result.get(0).getSuccessors(); 38 | // Assert.assertEquals(succ.size(), 1); 39 | // for (Taint s : succ) { 40 | // System.out.println(s.getStmt().toString()); 41 | // String fieldName = s.getStmt().getFieldRef().getField().getName(); 42 | // System.out.println(fieldName); 43 | // Assert.assertEquals(fieldName, "scriptName"); 44 | // } 45 | // } else if (methodString.contains("start")) { 46 | // // TODO: client, this.curator should also be tainted 47 | // System.out.println(methodString); 48 | // Assert.assertEquals(declaringClass, "org.apache.hadoop.util.curator.ZKCuratorManager"); 49 | // } else if (methodString.contains("loadSSLConfiguration")) { 50 | // System.out.println("##### HttpServer2 object using loadSSLConfiguration method"); 51 | // Assert.assertEquals(declaringClass.contains("HttpServer2"), true); 52 | // Set succ = result.get(0).getSuccessors(); 53 | // Assert.assertEquals(succ.size(), 1); 54 | // Set httpServerFieldNameSet = new HashSet<>(Arrays.asList("needsClientAuth", "keyStore", "keyStorePassword", 55 | // "keyStoreType", "keyPassword", "trustStore", "trustStorePassword", "trustStoreType", "excludeCiphers")); 56 | // for (Taint s : succ) { 57 | // String fieldName = s.getStmt().getFieldRef().getField().getName(); 58 | // Assert.assertEquals(httpServerFieldNameSet.contains(fieldName), true); 59 | // } 60 | // } 61 | // } 62 | // } 63 | // } 64 | } 65 | -------------------------------------------------------------------------------- /src/test/java/taintAnalysisTest/InterAnalysisTest.java: -------------------------------------------------------------------------------- 1 | package taintAnalysisTest; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | import soot.SootMethod; 6 | import taintAnalysis.*; 7 | import taintAnalysis.sourceSinkManager.ISourceSinkManager; 8 | import taintAnalysis.sourceSinkManager.SourceSinkManager; 9 | import utility.Config; 10 | 11 | import java.util.List; 12 | import java.util.Map; 13 | import java.util.Set; 14 | 15 | public class InterAnalysisTest extends TaintAnalysisTest { 16 | @Test 17 | public void testInterTaintAnalysis() { 18 | String[] cfg = Config.getCfg("test"); 19 | List srcPaths = Config.getSourcePaths(cfg); 20 | List classPaths = Config.getClassPaths(cfg); 21 | ISourceSinkManager sourceSinkManager = new SourceSinkManager(Config.getInterface(cfg)); 22 | TaintAnalysisDriver driver = new TaintAnalysisDriver(sourceSinkManager); 23 | InterAnalysisTransformer transformer = driver.runInterTaintAnalysis(srcPaths, classPaths, false); 24 | Map>>> methodSummary = transformer.getMethodSummary(); 25 | 26 | for (SootMethod method : methodSummary.keySet()) { 27 | // test dynamic binding in inheritance 28 | if (method.toString().contains("()>")) { 29 | List> taintList = methodSummary.get(method).get(Taint.getEmptyTaint()); 30 | Assert.assertEquals(2, taintList.size()); 31 | Assert.assertEquals(1, taintList.get(0).size()); // @this should be tainted 32 | } else if (method.toString().contains("()>")) { // subclass of Vehicle 33 | List> taintList = methodSummary.get(method).get(Taint.getEmptyTaint()); 34 | Assert.assertEquals(2, taintList.size()); 35 | Assert.assertEquals(1, taintList.get(0).size()); // @this should be tainted 36 | } else if (method.toString().contains("")) { 37 | List> taintList = methodSummary.get(method).get(Taint.getEmptyTaint()); 38 | Assert.assertEquals(3, taintList.size()); // @this, @return, @parameter0 39 | Assert.assertEquals(1, taintList.get(0).size()); // @this.a should be tainted 40 | } else if (method.toString().contains("")) { 41 | List> taintList = methodSummary.get(method).get(Taint.getEmptyTaint()); 42 | Assert.assertEquals(3, taintList.size()); // @this, @return, @parameter0 43 | Assert.assertEquals(0, taintList.get(0).size()); // @this.a should not be tainted 44 | } 45 | // test dynamic binding in interface 46 | else if (method.toString().contains("")) { 47 | List> taintList = methodSummary.get(method).get(Taint.getEmptyTaint()); 48 | Assert.assertEquals(3, taintList.size()); // @this, @return, @parameter0 49 | Assert.assertEquals(1, taintList.get(0).size()); 50 | for (Set s : taintList) { 51 | for (Taint t : s) { 52 | System.out.println("[Cat.dynamicBinding(Animal)] > " + t.toString()); 53 | } 54 | } 55 | } 56 | } 57 | 58 | List sources = transformer.getSources(); 59 | for (Taint t : sources) { 60 | System.out.println(t.toString()); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/test/java/taintAnalysisTest/SimpleIntraAnalysisTest.java: -------------------------------------------------------------------------------- 1 | package taintAnalysisTest; 2 | 3 | import org.junit.Test; 4 | import org.junit.Assert; 5 | import soot.SootMethod; 6 | import taintAnalysis.IntraAnalysisTransformer; 7 | import taintAnalysis.Taint; 8 | import taintAnalysis.TaintAnalysisDriver; 9 | import taintAnalysis.sourceSinkManager.ISourceSinkManager; 10 | import taintAnalysis.sourceSinkManager.SourceSinkManager; 11 | import utility.Config; 12 | 13 | import java.util.List; 14 | import java.util.Map; 15 | import java.util.Set; 16 | 17 | public class SimpleIntraAnalysisTest extends TaintAnalysisTest { 18 | 19 | @Test 20 | public void testIntraAnalysisSimpleTest() { 21 | String[] cfg = Config.getCfg("test"); 22 | List srcPaths = Config.getSourcePaths(cfg); 23 | List classPaths = Config.getClassPaths(cfg); 24 | ISourceSinkManager sourceSinkManager = new SourceSinkManager(Config.getInterface(cfg)); 25 | TaintAnalysisDriver driver = new TaintAnalysisDriver(sourceSinkManager); 26 | IntraAnalysisTransformer transformer = driver.runIntraTaintAnalysis(srcPaths, classPaths); 27 | List> results = transformer.getSourceLists(); 28 | for (List result : results) { 29 | if (result.size() > 0) { 30 | String method = result.get(0).getMethod().toString(); 31 | if (!method.contains("SimpleIntraAnalysisTest")) { 32 | continue; 33 | } 34 | if (method.contains("test4")) { 35 | Assert.assertEquals(0, result.size()); 36 | } else if (method.contains("test8")) { 37 | Assert.assertEquals(2, result.size()); 38 | } else if (!method.contains("callee")) { 39 | Assert.assertEquals(1, result.size()); 40 | } 41 | } 42 | } 43 | 44 | Map>>> methodSummary = transformer.getMethodSummary(); 45 | for (SootMethod method : methodSummary.keySet()) { 46 | if (method.toString().contains("")) { 47 | System.out.println("[TEST]> testing "); 48 | // test if taint on parameter is recorded 49 | List> taintList = methodSummary.get(method).get(Taint.getEmptyTaint()); 50 | Assert.assertEquals(5, taintList.size()); // callee function records for (@this, @returnVal, @parameter0, @parameter1, @parameter2, ) 51 | Assert.assertEquals(true, taintList.get(2).size() > 0); // book1 (@parameter0) should be tainted 52 | for (Taint t : taintList.get(1)) { 53 | System.out.println(t.toString()); 54 | } 55 | Assert.assertEquals(true, taintList.get(3).size() > 0); // book2 (@parameter1) should be tainted 56 | for (Taint t : taintList.get(2)) { 57 | System.out.println(t.toString()); 58 | } 59 | Assert.assertEquals(true, taintList.get(0).size() > 0); // @this should be tainted as well 60 | for (Taint t : taintList.get(4)) { 61 | System.out.println(t.toString()); 62 | } 63 | Assert.assertEquals(true, taintList.get(4).size() == 0); // int param shouldn't be tainted 64 | } else if (method.toString().contains("")) { 65 | System.out.println("[TEST]> testing "); 66 | List> taintList = methodSummary.get(method).get(Taint.getEmptyTaint()); 67 | Assert.assertEquals(5, taintList.size()); // callee function records for (@parameter0, @parameter1, @parameter2, @this, return value) 68 | // Assert.assertEquals(true, taintList.get(4).size() > 0); // return value should be tainted 69 | for (Taint t : taintList.get(4)) { 70 | System.out.println(t.toString()); 71 | } 72 | } 73 | } 74 | } 75 | 76 | } -------------------------------------------------------------------------------- /src/test/java/taintAnalysisTest/TaintAnalysisTest.java: -------------------------------------------------------------------------------- 1 | package taintAnalysisTest; 2 | 3 | public class TaintAnalysisTest { 4 | 5 | } --------------------------------------------------------------------------------