diff --git a/.github/ISSUE_TEMPLATE/FEATURE_TEMPLATE.yml b/.github/ISSUE_TEMPLATE/FEATURE_TEMPLATE.yml index 3671d2e510a..0d4f4039bf0 100644 --- a/.github/ISSUE_TEMPLATE/FEATURE_TEMPLATE.yml +++ b/.github/ISSUE_TEMPLATE/FEATURE_TEMPLATE.yml @@ -13,7 +13,7 @@ body: attributes: label: Missing Feature description: Please describe the wanted functionality in detail. If possible enhance it with examples and/or scientific publications. - value: "" + value: Please describe the missing feature here.. validations: required: true diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 7a8fc28c52e..463ccc35a2b 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -51,11 +51,7 @@ jobs: python-version: 3.x # install dependencies - - run: pip install mike - - run: pip install mkdocs-material - - run: pip install mkdocs-tooltips - - run: pip install git+https://github.com/RedisLabs/mkdocs-include.git - - run: pip install git+https://github.com/swissiety/LspLexer4Pygments.git + - run: pip install mike mkdocs-material mkdocs-tooltips git+https://github.com/RedisLabs/mkdocs-include.git git+https://github.com/swissiety/LspLexer4Pygments.git # grab latest release url of the JimpleLSP jar and download it - run: curl -s -L -o ./jimplelsp.jar $(curl -s https://api.github.com/repos/swissiety/jimpleLsp/releases/latest | grep 'browser_download_url".*jar"' | cut -d ':' -f 2,3 | tr -d \") @@ -68,8 +64,8 @@ jobs: git config --local user.email "github-actions[bot]@users.noreply.github.com" git config --local user.name "github-actions[bot]" - # sanitive head_ref name - - run: echo "DOC_VERSION_NAME=$(echo ${{ github.head_ref }} | sed "s/[^[:alnum:]-]/_/g" )" >> $GITHUB_ENV + # sanitize head_ref name + - run: echo "DOC_VERSION_NAME=$(echo ${{ github.head_ref }} | sed "s/[^([[:alnum:]_.-]/_/g" )" >> $GITHUB_ENV # on push to develop branch - keep a doc around for develop to show the current state - name: deploy doc in subdirectory @@ -86,7 +82,7 @@ jobs: uses: marocchino/sticky-pull-request-comment@v2 with: message: | - You updated the documentation - [Doc Preview](https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/${{ env.DOC_VERSION_NAME }}_preview/). + [Documentation Preview](https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/${{ env.DOC_VERSION_NAME }}_preview/). # on PR close - delete preview - name: delete the deployed preview diff --git a/README.md b/README.md index cda231d7048..c1d5f8c158e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- +

# SootUp library ![Java CI with Maven](https://github.com/soot-oss/SootUp/workflows/Java%20CI%20with%20Maven/badge.svg?branch=develop) [![codecov](https://codecov.io/gh/soot-oss/SootUp/branch/develop/graph/badge.svg?token=ELA7U7IAWD)](https://codecov.io/gh/soot-oss/SootUp) [![javadoc](https://javadoc.io/badge2/org.soot-oss/sootup.core/javadoc.svg)](https://javadoc.io/doc/org.soot-oss/sootup.core) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.soot-oss/sootup.core/badge.svg)](https://central.sonatype.com/artifact/org.soot-oss/sootup) @@ -47,7 +47,7 @@ You want to collaborate? Please read our [coding guidelines and the contributors ## Publications [the SootUp paper](https://doi.org/10.1007/978-3-031-57246-3_13) explains further details and the design decision behind SootUp. -[Preprint](/docs/SootUp-paper.pdf) is also available. +[Preprint](/docs/assets/SootUp-paper.pdf) is also available. If you use SootUp in your research work, feel free to cite it as follows: diff --git a/docs/analysisinput.md b/docs/analysisinput.md new file mode 100644 index 00000000000..4bcd03312b6 --- /dev/null +++ b/docs/analysisinput.md @@ -0,0 +1,147 @@ +# Analysis Input +i.e. What should be analyzed. An `AnalysisInputLocation` points to code input SootUp can analyze. +We ship multiple Implementations that can handle different input. + +Additionally you can specify a SourceType. This determines what is considered e.g. in the CallGraphs generation. +Further you can specify a List of [BodyInterceptors](bodyinterceptors.md), which will optimize the raw Jimple IR that was transformed from the input. + +### Java Runtime +#### Java <=8 +The `DefaultRTJaAnalysisInputLocation` points to the rt.jar of the executing JVM. + + +```java +AnalysisInputLocation inputLocation = new DefaultRTJaAnalysisInputLocation(); +JavaView view = new JavaView(inputLocation); +``` + +To include a different Java Runtime library point to any rt.jar via a `JavaClassPathAnalysisInputLocation` as its a usual .jar file. + +#### Java >=9 +The `JRTFilesystemAnalysisInputLocation` points to the jigsawed java runtime of the executing JVM. + +```java +AnalysisInputLocation inputLocation = new JrtFileSystemAnalysisInputLocation(); +JavaView view = new JavaView(inputLocation); +``` + + +!!! info "If you have errors like Java.lang.String, Java.lang.Object, ... you are most likely missing this AnalysisInputLocation." + +### Java Bytecode +File-Extensions: `.class, .jar, .war` + +The `JavaClassPathAnalysisInputLocation` is the equivalent of the classpath you would pass to the java executable i.e. point to root(s) of package(s). + +=== "Directory" + ```java + AnalysisInputLocation inputLocation = + new JavaClassPathAnalysisInputLocation("target/"); // points to + JavaView view = new JavaView(inputLocation); + ``` + +=== ".jar File" + ```java + AnalysisInputLocation inputLocation = new JavaClassPathAnalysisInputLocation("myCode.jar"); + JavaView view1 = new JavaView(inputLocation); + + // if you want to analyze a specific language level of a multi release jar + AnalysisInputLocation inputLocation = + new MultiReleaseJarAnalysisInputLocation("myCode.jar", new JavaLanguage(10) ); + JavaView view2 = new JavaView(inputLocation); + ``` + +=== ".class File" + ```java + // if you omit the package structure while pointing to a file, + // you have to pass the omitted directories as a parameter + AnalysisInputLocation inputLocation = new PathBasedAnalysisInputLocation. + ClassFileBasedAnalysisInputLocation("Banana.class", "packageName.subPackage", SourceType.Application); + JavaView view = new JavaView(inputLocation); + ``` + +=== "Complete class path" + ```java + String cp = "myCode.jar" + File.pathSeparator + "dependency.jar" + File.pathSeparator + "target/classes/"; + AnalysisInputLocation inputLocation = new JavaClassPathAnalysisInputLocation(cp); + JavaView view = new JavaView(inputLocation); + ``` + +### Java Sourcecode +File-Extensions: `.java` + +With the `OTFCompileAnalysisInputLocation` you can point directly to .java files or pass a String with Java sourcecode. +The AnalysisInputLocation delegates the data to the `JavaCompiler` and transform the bytecode from the compiler to Jimple. + +=== "Single File" + ```java + AnalysisInputLocation inputLocation = new OTFCompileAnalysisInputLocation("Banana.java"); + JavaView view = new JavaView(inputLocation); + ``` + +=== "Multiple Files" + ```java + List files = Arrays.asList(Paths.get("Apple.java"), Paths.get("Banana.java")); + AnalysisInputLocation inputLocation = new OTFCompileAnalysisInputLocation(files); + JavaView view = new JavaView(inputLocation); + ``` +=== "File as String" + ```java + String content = "public class B{ }"; + AnalysisInputLocation location = new OTFCompileAnalysisInputLocation("B.java", content ); + JavaView view = new JavaView(location); + ``` + +`JavaSourcePathInputLocation` [***experimental!***]{Has huge problems with exceptional flow!} - points to a directory that is the root source directory (containing the package directory structure). + +### Jimple +File-Extensions: `.jimple` + +The `JimpleAnalysisInputLocation` needs a Path to a .jimple file or a directory. + +```java +Path path = Paths.get("Banana.java"); +AnalysisInputLocation jimpleLocation = new JimpleAnalysisInputLocation(path); +JavaView view = new JavaView(jimpleLocation); +``` + + +### Android Bytecode +File-Extensions: `.apk` + +The `ApkAnalysisInputLocation` currently uses dex2jar internally + +```java +Path path = Paths.get("Banana.apk"); +AnalysisInputLocation inputLocation = new ApkAnalysisInputLocation(path); +JavaView view = new JavaView(inputLocation); +``` + +!!! info "A SootUp solution to directly generate Jimple is WIP!" + + +### Combining Multiple AnalysisInputLocations +But what if I want to point to multiple AnalysisInputLocations? + +```java +AnalysisInputLocation mainJar = new JavaClassPathAnalysisInputLocation("myCode.jar"); +AnalysisInputLocation jarA = new JavaClassPathAnalysisInputLocation("dependencyA.jar"); +AnalysisInputLocation jarB = new JavaClassPathAnalysisInputLocation("dependencyB.jar"); + +List inputlocationList = Arrays.asList(mainJar, jarA, jarB); + +JavaView view = new JavaView(inputlocationList); +``` +!!! note "Of course you can combine different types of `AnalysisInputLocation`s as well!" + + +### Maven Project as Analysis Input in SootUp +This uses `#!shell mvn compile` + `JavaClassPathAnalysisInputLocation` under the hood to include a maven project. +```java + TODO: let the code sail with the upstream boat to this doc. +``` + +Unfortunately its harder to extract the path of the binary result of Gradle projects in a unified way for all kinds of models - If you have a solution are looking forward to merge your contribution :-). + +### Java cli arguments to configure SootUp +We created a [Utility](tool_setup.md) that parses a String of java command line arguments and configures SootUp respectively. \ No newline at end of file diff --git a/docs/announce.md b/docs/announcement.md similarity index 82% rename from docs/announce.md rename to docs/announcement.md index 76ecf8b2fe8..78941b91c18 100644 --- a/docs/announce.md +++ b/docs/announcement.md @@ -12,17 +12,17 @@ SootUp is a library that can easily be included in other projects, leaving those Below is an overview of what’s new. -* Library by default, framework as an option -* Modular Architecture, no more singletons -* New source code frontend -* Immutable Jimple IR -* Greatly increased testability and test coverage +- Library by default, framework as an option +- Modular Architecture, no more singletons +- New source code frontend +- Immutable Jimple IR +- Greatly increased testability and test coverage ![Coverage](https://camo.githubusercontent.com/adc4ab244f7c0c2b2f3fec0a6e5d778421ddc0be7f89a608c16533c9a964766f/68747470733a2f2f636f6465636f762e696f2f67682f736f6f742d6f73732f536f6f7455702f6272616e63682f646576656c6f702f67726170682f62616467652e7376673f746f6b656e3d454c4137553749415744) SootUp is not a drop-in replacement for Soot! Due to its completely new architecture and API it is essentially an almost complete rewrite. For a while, Soot and SootUp will coexist, as many existing tools depend on Soot, yet our maintenance efforts will henceforth be focused on SootUp, not Soot, and on extending SootUp with those capabilities that people still find missing. For now, we recommend using SootUp for greenfield projects. For more details, check out -* The SootUp home page: https://soot-oss.github.io/SootUp/, and -* The SootUp repository: https://github.com/soot-oss/SootUp/ + +[This Page ;-)](https://soot-oss.github.io/SootUp/) and The SootUp repository: [https://github.com/soot-oss/SootUp/](https://soot-oss.github.io/SootUp/) We are very much looking forward to your feedback and feature requests. To this end, best create appropriate issues in the repository. diff --git a/docs/SootUp-paper.pdf b/docs/assets/SootUp-paper.pdf similarity index 100% rename from docs/SootUp-paper.pdf rename to docs/assets/SootUp-paper.pdf diff --git a/docs/figures/CopyPropagator Example_1.png b/docs/assets/figures/CopyPropagator Example_1.png similarity index 100% rename from docs/figures/CopyPropagator Example_1.png rename to docs/assets/figures/CopyPropagator Example_1.png diff --git a/docs/figures/CopyPropagator Example_2.png b/docs/assets/figures/CopyPropagator Example_2.png similarity index 100% rename from docs/figures/CopyPropagator Example_2.png rename to docs/assets/figures/CopyPropagator Example_2.png diff --git a/docs/figures/DominanceFinder Example.png b/docs/assets/figures/DominanceFinder Example.png similarity index 100% rename from docs/figures/DominanceFinder Example.png rename to docs/assets/figures/DominanceFinder Example.png diff --git a/docs/figures/EmptySwitchEliminator Example.png b/docs/assets/figures/EmptySwitchEliminator Example.png similarity index 100% rename from docs/figures/EmptySwitchEliminator Example.png rename to docs/assets/figures/EmptySwitchEliminator Example.png diff --git a/docs/figures/LocalLiveness Example.png b/docs/assets/figures/LocalLiveness Example.png similarity index 100% rename from docs/figures/LocalLiveness Example.png rename to docs/assets/figures/LocalLiveness Example.png diff --git a/docs/figures/LocalPacker Example.png b/docs/assets/figures/LocalPacker Example.png similarity index 100% rename from docs/figures/LocalPacker Example.png rename to docs/assets/figures/LocalPacker Example.png diff --git a/docs/figures/LocalSplitter Example_1.png b/docs/assets/figures/LocalSplitter Example_1.png similarity index 100% rename from docs/figures/LocalSplitter Example_1.png rename to docs/assets/figures/LocalSplitter Example_1.png diff --git a/docs/figures/LocalSplitter Example_2.png b/docs/assets/figures/LocalSplitter Example_2.png similarity index 100% rename from docs/figures/LocalSplitter Example_2.png rename to docs/assets/figures/LocalSplitter Example_2.png diff --git a/docs/figures/SSA Example_1.png b/docs/assets/figures/SSA Example_1.png similarity index 100% rename from docs/figures/SSA Example_1.png rename to docs/assets/figures/SSA Example_1.png diff --git a/docs/figures/SSA Example_2.png b/docs/assets/figures/SSA Example_2.png similarity index 100% rename from docs/figures/SSA Example_2.png rename to docs/assets/figures/SSA Example_2.png diff --git a/docs/figures/TrapTightener Example.png b/docs/assets/figures/TrapTightener Example.png similarity index 100% rename from docs/figures/TrapTightener Example.png rename to docs/assets/figures/TrapTightener Example.png diff --git a/docs/figures/UnreachableCodeEliminator Example.png b/docs/assets/figures/UnreachableCodeEliminator Example.png similarity index 100% rename from docs/figures/UnreachableCodeEliminator Example.png rename to docs/assets/figures/UnreachableCodeEliminator Example.png diff --git a/docs/bodyinterceptors.md b/docs/bodyinterceptors.md index 7963174a0fa..4039ee4508e 100644 --- a/docs/bodyinterceptors.md +++ b/docs/bodyinterceptors.md @@ -9,7 +9,8 @@ The "raw" generated Jimple from the Bytecodefrontend needs a lot improvements - - The Locals we get from the Java bytecode are typically untyped. Therefore we have to augment the Local types which is done by the TypeAssigner. - t.b.c. -Method scoped optimisations: +Optimizations (method scope) + - ConditionalBranchFolder: removes tautologic ifs that are always true/false - if we can determine it in the scope of the method. - EmptySwitchEliminator: removes switches that are not really switching - ConstantPropagatorAndFolder: calculates constant values before runtime @@ -17,16 +18,14 @@ Method scoped optimisations: - UnreachableCodeEliminator: speaks for itself. - TrapTightener -Make Local names standardized: +Standardize Jimple appearance + - LocalNameStandardizer: numbers Locals with the scheme: type-initial + number of type occurence !!! info "Soot Equivalent" - [BodyTransformer](https://github.com/soot-oss/soot/blob/develop/src/main/java/soot/BodyTransformer.java) -Below, we show how these BodyInterceptors work for the users who are interested in their internal workings. - ### LocalSplitter LocalSplitter is aBodyInterceptorthat attempts to identify and separate uses of a local variable (as definition) that are independent of each other by renaming local variables. @@ -34,7 +33,7 @@ LocalSplitter is aBodyInterceptorthat attempts to identify and sepa Example 1: -![LocalSplitter Example_1](./figures/LocalSplitter%20Example_1.png) +![LocalSplitter Example_1](assets/figures/LocalSplitter%20Example_1.png) As shown in the example above, the local variablel1is defined twice. It can be split up into two new local variables: l1#1 and l1#2 because the both definitions are independent of each other. @@ -45,7 +44,7 @@ Look for foldable navigation and tabs for showing old vs new Example 2: -![LocalSplitter Example_2](./figures/LocalSplitter%20Example_2.png) +![LocalSplitter Example_2](assets/figures/LocalSplitter%20Example_2.png) In the second example, the local variablel2is defined thrice. But it cannot be split up into three new local variables as in the first example, because its definitions in the if-branches are not independent of each other. Therefore, it can only be split up into two local variables as shown in the figure. @@ -57,7 +56,7 @@ LocalPacker is aBodyInterceptorthat attempts to minimize the number Example: -![LocalPacker Example](./figures/LocalPacker%20Example.png) +![LocalPacker Example](assets/figures/LocalPacker%20Example.png) In the given example above, the local variablesl1,l3are summarized to be one local variablel1, because they have the same type without interference with each other. Likewise, the local variablesl2,l4andl5are summarized to be another local variablel2. Although the local variablel0doesn't interfere any other local variables, it cannot be summed up with other local variables because of its distinctive type. @@ -70,7 +69,7 @@ TrapTightener is aBodyInterceptorthat shrinks the protected area co Example: -![TrapTightener Example](./figures/TrapTightener%20Example.png) +![TrapTightener Example](assets/figures/TrapTightener%20Example.png) We assume in the example above that only theStmt:l2 := 2might throw an exception caught by theTrapwhich is labeled withlabel3. In the jimple body before running the TrapTightener, the protected area covered by the Trap contains threeStmts:l1 := 1; l2 := 2; l2 := 3. But an exception could only arise at theStmt:l2 := 2. After the implementation of TrapTightener, we will get a contractible protected area which contains only theStmtthat might throw an exception, namely theStmt:l2 := 2. @@ -82,7 +81,7 @@ EmptySwitchEliminator is aBodyInterceptorthat removes empty switch Example: -![EmptySwitchEliminator Example](./figures/EmptySwitchEliminator%20Example.png) +![EmptySwitchEliminator Example](assets/figures/EmptySwitchEliminator%20Example.png) As shown in the example above, the switch statement in the jimple body always takes the default action. After running EmptySwitchEliminator, the switch statement is replaced with aGotoStmtto the default case. @@ -94,7 +93,7 @@ UnreachableCodeEliminator is aBodyInterceptorthat removes all unrea Example: -![UnreachableCodeEliminator Example](./figures/UnreachableCodeEliminator%20Example.png) +![UnreachableCodeEliminator Example](assets/figures/UnreachableCodeEliminator%20Example.png) Obviously, the code segmentl2 = 2; l3 = 3;is unreachable. It will be removed after running the UreachableCodeEliminator. @@ -106,7 +105,7 @@ CopyPropagator is aBodyInterceptorthat supports the global copy pro Example for global copy propagation: -![UnreachableCodeEliminator Example](./figures/CopyPropagator%20Example_1.png) +![UnreachableCodeEliminator Example](assets/figures/CopyPropagator%20Example_1.png) Consider a code segment in the following form: @@ -125,7 +124,7 @@ In the example for global copy propagation, the first usedl1is repl Example for constant propagation: -![CopyPropagator Example_1](figures/CopyPropagator%20Example_2.png) +![CopyPropagator Example_1](assets/figures/CopyPropagator%20Example_2.png) Constant propagation is similar to copy propagation. Consider a code segment in the following form: @@ -169,8 +168,8 @@ StaticSingleAssignmentFormer is aBodyInterceptorthat transforms jim Example: -![SSA Example_1](./figures/SSA%20Example_1.png) +![SSA Example_1](assets/figures/SSA%20Example_1.png) -![SSA Example_2](./figures/SSA%20Example_2.png) +![SSA Example_2](assets/figures/SSA%20Example_2.png) In the given example, the StaticSingleAssignmentFormer assigns eachIdentityStmtandAssignStmtto a new local variable . And each use uses the local variable which is most recently defined. Sometimes, it is impossible to determine the most recently defined local variable for a use in a join block. In this case, the StaticSingleAssignmentFormer will insert aPhiStmtin the front of the join block to merge all most recently defined local variables and assign them a new local variable. \ No newline at end of file diff --git a/docs/advanced-topics.md b/docs/builtin-analyses.md similarity index 78% rename from docs/advanced-topics.md rename to docs/builtin-analyses.md index 28e67ac89fc..6878e646810 100644 --- a/docs/advanced-topics.md +++ b/docs/builtin-analyses.md @@ -1,19 +1,20 @@ -# Functionalities and Utilities +# BuiltIn Analyses +More to come! -#### LocalLivenessAnalyser +### LocalLivenessAnalyser LocalLivenessAnalyser is used for querying for the list of live local variables before and after a given Stmt. Example: -![LocalLiveness Example](./figures/LocalLiveness%20Example.png) +![LocalLiveness Example](assets/figures/LocalLiveness%20Example.png) The live local variables before and after each Stmt will be calculated after generating an instance of LocalLivenessAnalyser as shown the example above. They can be queried by using the methods getLiveLocalsBeforeStmt and getLiveLocalsAfterStmt. -#### DominanceFinder +### DominanceFinder DomianceFinder is used for querying for the immediate dominator and dominance frontiers for a given basic block. -Example: ![DominanceFinder Example](figures/DominanceFinder%20Example.png) +Example: ![DominanceFinder Example](assets/figures/DominanceFinder%20Example.png) After generating an instance of DominanceFinder for a BlockGraph, we will get the immediate dominator and dominance frontiers for each basic block. The both properties can be queried by using the methodsgetImmediateDominatorandgetDominanceFrontiers. diff --git a/docs/call-graph-construction.md b/docs/callgraphs.md similarity index 72% rename from docs/call-graph-construction.md rename to docs/callgraphs.md index 79d87e55622..a28fb696935 100644 --- a/docs/call-graph-construction.md +++ b/docs/callgraphs.md @@ -10,8 +10,9 @@ Below, we show how to create a type hierarchy: === "SootUp" ```java + String cpString = "src/test/resources/Callgraph/binary"; List inputLocations = new ArrayList(); - inputLocations.add(new JavaClassPathAnalysisInputLocation("src/test/resources/Callgraph/binary")); + inputLocations.add(new JavaClassPathAnalysisInputLocation(cpStr)); inputLocations.add(new DefaultRTJarAnalysisInputLocation()); JavaView view = new JavaView(inputLocations); @@ -20,10 +21,10 @@ Below, we show how to create a type hierarchy: === "Soot" ```java - String targetTestClassName = target.exercise1.Hierarchy.class.getName(); - G.reset(); String userdir = System.getProperty("user.dir"); String sootCp = userdir + File.separator + "target" + File.separator + "test-classes"+ File.pathSeparator + "lib"+File.separator+"rt.jar"; + String targetTestClassName = target.exercise1.Hierarchy.class.getName(); + G.reset(); Options.v().set_whole_program(true); Options.v().set_soot_classpath(sootCp); Options.v().set_no_bodies_for_excluded(true); @@ -41,25 +42,35 @@ Below, we show how to create a type hierarchy: ``` ## Defining an Entry Method -All the call graph construction algorithms require an entry method to start with. In java application, you usually define the main method. However, it is possible to define arbitrary entry methods depending on your needs. Below, we show how to define such an entry method: +All call graph construction algorithms require an entry method to start with. In java application, you usually define the main method. However, it is possible to define arbitrary entry methods depending on your needs. Below, we show how to define such an entry method: -=== "SootUp" +=== "SootUp (performant)" ```java - JavaClassType classTypeA = view.getIdentifierFactory().getClassType("A"); + JavaClassType classTypeA = view.getIdentifierFactory().getClassType("packageNameA.A"); MethodSignature entryMethodSignature = view.getIdentifierFactory() .getMethodSignature( classTypeA, - JavaIdentifierFactory.getInstance() - .getMethodSubSignature( - "calc", VoidType.getInstance(), Collections.singletonList(classTypeA))); + "calc", + VoidType.getInstance(), + Collections.singletonList(classTypeA) + ); ``` - + +=== "SootUp (alternative)" + + ```java + String methodSigStr = " System.out.println(entryMethodSignature + " may call " + tgt); ``` === "Soot" @@ -90,7 +100,7 @@ You can construct a call graph with CHA as follows: while (targets.hasNext()) { SootMethod tgt = (SootMethod)targets.next(); System.out.println(src + " may call " + tgt); - } + } ``` ## Rapid Type Analysis @@ -100,13 +110,12 @@ You can construct a call graph with RTA as follows: === "SootUp" ```java - CallGraphAlgorithm rta = - new RapidTypeAnalysisAlgorithm(view); + CallGraphAlgorithm rta = new RapidTypeAnalysisAlgorithm(view); - CallGraph cg = - rta.initialize(Collections.singletonList(entryMethodSignature)); + CallGraph cg = rta.initialize(Collections.singletonList(entryMethodSignature)); - System.out.println(cg); + cg.callsFrom(entryMethodSignature).stream() + .forEach(tgt -> System.out.println(entryMethodSignature + " may call " + tgt); ``` === "Soot" @@ -127,13 +136,16 @@ You can construct a call graph with RTA as follows: } ``` + +## Qilin Pointer Analysis + +Qilin builds a call graph on the fly with the pointer analysis. +You can construct a call graph with Qilin as follows: + +==="SootUp" + +```java +String MAINCLASS = "dacapo.antlr.Main"; // just an example +PTAPattern ptaPattern = new PTAPattern("insens"); // "2o"=>2OBJ, "1c"=>1CFA, etc. +PTA pta = PTAFactory.createPTA(ptaPattern, view, MAINCLASS); +pta.run(); +CallGraph cg = pta.getCallGraph(); +``` \ No newline at end of file diff --git a/docs/css/customizetheme.css b/docs/css/customizetheme.css new file mode 100644 index 00000000000..00a4cb6d4ea --- /dev/null +++ b/docs/css/customizetheme.css @@ -0,0 +1,172 @@ +:root { + --md-primary-fg-color: hsla(var(--md-hue), 0%, 100%, 1); + --md-primary-fg-color--light: hsla(var(--md-hue), 0%, 100%, 0.7); + --md-primary-fg-color--dark: hsla(var(--md-hue), 0%, 0%, 0.07); + --md-primary-bg-color: hsla(var(--md-hue), 0%, 0%, 0.87); + --md-primary-bg-color--light: hsla(var(--md-hue), 0%, 0%, 0.54); + + --md-accent-fg-color: rgba(255,140,0, 1); +} +[data-md-color-accent=indigo]{ + --md-accent-fg-color: rgba(255,140,0, 1); +} + +.md-main__inner { + margin-top: 0; + margin-bottom: 5em; +} + +.md-typeset{ + font-size: 12pt; +} + +.md-typeset a { + color: rgba(255,140,0, 0.8); +} + +.md-nav__link[for]:focus, .md-nav__link[for]:hover, .md-nav__link[href]:focus, .md-nav__link[href]:hover { + color: rgba(255,140,0, 0.8); +} + +.md-typeset h1>a, .md-typeset h2>a, .md-typeset h3>a { + text-decoration: underline; +} + +.md-typeset a:hover { + color: rgba(255,140,0, 0.4); +} + +.md-typeset h1, .md-typeset h2, .md-typeset h3{ + color:rgba(255,140,0, 1); + font-weight: normal; +} + +.md-typeset .admonition, .md-typeset .admonition.info, .md-typeset .admonition.example, .md-typeset .admonition, .md-typeset .admonition.note{ + border-color: rgba(255,140,0, 0.1); + margin-bottom: 3em; +} + +.md-typeset .admonition>.admonition-title:before, .md-typeset .admonition>summary:before{ + background-color: rgba(255,140,0, 1); +} + +.md-typeset .admonition>.admonition-title, .md-typeset .admonition>.admonition-title{ + background: rgba(255,140,0, 0.1); + color: rgba(255,140,0, 1); +} +.md-typeset .admonition, .md-typeset details { + border-color: rgba(255,140,0, 1); + box-shadow: none; + +} + +.md-nav label.md-nav__title{ + color:rgba(255,140,0, 1); +} +.md-nav__item--section>.md-nav__link[for] { + color:rgba(255,140,0, 1); +} + + +.md-nav__item a{ + color: #222; +} + +.md-nav__link.md-nav__link--active{ + color: rgba(255,140,0, 1); +} + + +.md-sidebar.md-sidebar--primary .md-sidebar__scrollwrap{ + border-right: 1px solid #DDD; + margin-right: 2em; + padding-right: 2em; +} +.md-sidebar.md-sidebar--secondary .md-sidebar__scrollwrap{ + border-left: 1px solid #DDD; + margin-left: 2em; + padding-left: 2em; +} + +.md-sidebar.md-sidebar--secondary .md-nav__item a { + padding: 0; +} + +.md-sidebar.md-sidebar--secondary .md-nav__item .md-nav{ + padding-bottom: 1em; +} + +.md-search__form{ + background: #EEE; +} + +.md-typeset code { + padding:0.2em; + margin:0.2em; + font-size: 10pt; + background-color: #FCFCFC; +} + +.tabbed-set.tabbed-alternate{ + background: rgba(255,140,0, 0.1); + color: rgba(255,140,0, 1); + margin-bottom: 3em; +} + +.md-typeset .tabbed-labels>label>[href]:first-child{ + color: rgba(255,140,0, 1); +} + +.md-footer{ + margin-top: 2em; + padding:1em; + background: #EEE; +} + +.md-footer-meta.md-typeset, .md-footer-meta .md-copyright{ + background: none; + color:#000; +} + +html .md-footer-meta.md-typeset a{ + color:#666; +} + +.md-typeset table:not([class]), .md-typeset table:not([class]) td{ + border-collapse: collapse; + border:none; +} + +.admonition .highlighttable{ + margin: 0; + border: none; +} +.md-typeset .admonition .highlighttable, .md-typeset details .highlighttable{ + +} + +.highlighttable{ + border:1px solid rgba(255,140,0, 0.1); + border-top: 6px solid rgba(255,140,0, 0.1); +} + +.admonition .highlighttable, .tabbed-set .highlighttable{ + border-top:none; +} + +.highlighttable .linenos{ + background-color: #FCFCFC;; + font-size: 10pt; +} + +#developedBy{ + font-size: 11pt; +} + +.tooltip{ + color: rgba(255,140,0, 0.8); +} + +p{ + margin-bottom:1.5em; +} \ No newline at end of file diff --git a/docs/examples.md b/docs/examples.md new file mode 100644 index 00000000000..5fe5be75b1e --- /dev/null +++ b/docs/examples.md @@ -0,0 +1,91 @@ +# SootUp Example projects +Some examples that use SootUp to get insights about a Java program. + +- Basic setup [Example](https://github.com/soot-oss/SootUp-Examples/blob/main/BasicSetupExample/src/main/java/sootup/examples/BasicSetup.java) +- Configure a BodyInterceptor [Example](https://github.com/soot-oss/SootUp-Examples/blob/main/BodyInterceptorExample/src/main/java/sootup/examples/BodyInterceptor.java) +- CallGraph [Example](https://github.com/soot-oss/SootUp-Examples/blob/main/CallgraphExample/src/main/java/sootup/examples/CallgraphExample.java) +- Class Hierarchy Algoritm [Example](https://github.com/soot-oss/SootUp-Examples/blob/main/ClassHierarchyExample/src/main/java/sootup/examples/ClassHierarchy.java) +- Replace a SootMethod of a SootClass [Example](https://github.com/soot-oss/SootUp-Examples/blob/main/MutatingSootClassExample/src/main/java/sootup/examples/MutatingSootClass.java) + + +!!! info "Download" + The Examples can be cloned or downloaded from our [Example Repository](https://github.com/soot-oss/SootUp-Examples.git). + + + + \ No newline at end of file diff --git a/docs/faq.md b/docs/faq.md index f9c75c88b54..e7f2efa5e51 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -42,12 +42,14 @@ or create a convenient link with the exported stmtgraph as HTTP GET Parameter DotExporter.createUrlToWebeditor( stmtgraph ); ``` +### The Sourcecodefrontend... +is in a experimental state! If you wish to use it, please consider to contribute. ### Is there a way to use code exploration and syntax highlighting features in my IDE for .jimple files? -Try [JimpeLsp](https://github.com/swissiety/JimpleLsp). +Try [JimpeLsp](https://github.com/swissiety/JimpleLsp) or the [vscode plugin](https://marketplace.visualstudio.com/items?itemName=swissiety.jimplelsp) ### Is there a way to use syntax highlighting of .jimple in my paper, thesis, ...? -Have a look at [LspLexer4Pygments](https://github.com/swissiety/LspLexer4Pygments). +Have a look at [LspLexer4Pygments](https://github.com/swissiety/LspLexer4Pygments). Its the same syntax highlighting you see here in the documentation. You can export it to LaTex as well. ### How to ... add an entry in this list? i.e. Your question is not answered here? Feel free to start a [Discussion](https://github.com/soot-oss/SootUp/discussions). diff --git a/docs/getting-started.md b/docs/getting-started.md index 2f9df6b80a4..6ca98dbe806 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -1,34 +1,17 @@ -# General Usage of SootUp -This page walks you through the core data structures, as well as shows how to get started with SootUp. - -## The core datastructures +# First Steps with SootUp Before you get started with the SootUp library, it helps to learn about the following core data structures: -- `Language`: represents the programming language of the analyzed code. - -- `AnalysisInputLocation`: points to the target code to be analyzed. - -!!! info "Soot Equivalent" - - It corresponds to the `cp` option, which specifies the classpath for Soot to find classes to be analyzed. - -- `View`: presents the code/classes under analysis. - -!!! info "Soot Equivalent" - - It corresponds to the `Scene` class, but it is not a singleton. So it is possible to instantiate multiple views simultaneously. +- [`AnalysisInputLocation`]{It corresponds to the `cp` option, which specifies the classpath for Soot to find classes to be analyzed.} + : points to the target code that shall be loaded into the `View`. -- `Scope`: defines the scope of the `View`. By default, the `View` is created with all code found on the `AnalysisInputLocation` specified for the `Project` instance. +- [`View`]{Corresponds to the `Scene` class, but it is not a singleton. So it is possible to instantiate multiple views simultaneously.}: +handles the representation of the code you configured it to analyze. -- `SootClass`: represents a class loaded into the `View`. - -- `SootMethod`: represents a method of a class. - -- `SootField`: represents a field of a class. - -- `Body`: represents a method body in Jimpe. - -- `StmtGraph`: represents the control flow graph of a method body in Jimple statements. +- `SootClass`: represents a class. Can be loaded from the View via a `ClassType` identifier. +- `SootMethod`: represents a method of a class - loaded from the View via a `MethodSignature` identifier. +- `SootField`: represents a field of a class - loaded from the View via a `FieldSignature` identifier. +- `Body`: represents a method body of a `SootMethod`. +- `StmtGraph`: represents the control flow graph of a `Body`. `Stmt`'s represent actual Instructions. ## Creating a View @@ -45,11 +28,10 @@ You can use bytecode analysis typically when you do not have access to the sourc If you have access to the source code, it is also possible to create a view for analyzing source code. Following example shows how to create view for analyzing Java source code. -!!! info "Experimental" - - The source code frontend is experimental and should only be used for testing purposes. You should compile the code for analysis first and use the bytecode frontend instead. +!!! info "Experimental! - Create a view to analyze Java source code" -!!! example "Create a view to analyze Java source code" + The source code frontend is experimental and should only be used for testing purposes. + Usually you should compile the code for analysis first and use the bytecode frontend instead (see above). ~~~java AnalysisInputLocation inputLocation = @@ -60,7 +42,7 @@ If you have access to the source code, it is also possible to create a view for If you have a [Jimple](../jimple) file, you can create a view for analyzing jimple code directly. Following example shows how to create a view for analyzing jimple code. -!!! example "Create a project to analyze jimple code" +!!! example "Create a view to analyze jimple code" ~~~java Path pathToJimple = Paths.get("path2Jimple"); @@ -79,10 +61,10 @@ If you have a [Jimple](../jimple) file, you can create a view for analyzing jimp By default, whenever a class is retrieved, it will be permanently stored in a cache. If you do not want retrieved classes to be stored indefinetly, you can instead provide a different `CacheProvider` to the created view. -To for example use an `LRUCache` instead, which stores at most 50 classes, and always replaces the least recently used class by a newly retrieved one, use the following call: +To for example use an `LRUCache` instead, which stores at most e.g. 50 classes, and always replaces the least recently used class by a newly retrieved one, use the following call: ```java -JavaView view = new JavaView(Collections.singletonList(inputLocation), new LRUCacheProvider(50)); +JavaView view = new JavaView(inputLocations, new LRUCacheProvider(50)); ``` @@ -136,17 +118,25 @@ Once we have a `ClassType` that identifies the `HelloWorld` class, we can use it Like the classes, methods also have an identifier which we call `MethodSignature`. For instance, we can define the method signature for identifying the `main` method of the `HelloWorld` class as follows: !!! example "Defining a MethodSignature" - - ```java - MethodSignature methodSignature = - view - .getIdentifierFactory() - .getMethodSignature( - "main", // method name - classType, - "void", // return type - Collections.singletonList("java.lang.String[]")); // args - ``` + === "Pure" + ```java + MethodSignature methodSignature = + view + .getIdentifierFactory() + .getMethodSignature( + "main", // method name + classType, + "void", // return type + Collections.singletonList("java.lang.String[]")); // args + ``` + === "Parse from String" + ```java + MethodSignature methodSignature = + view + .getIdentifierFactory() + .parseMethodSignature( + ""); + ``` Once we have a `MethodSignature` that identifies the `main` method of the `HelloWorld` class, we can use it to retrieve the corresponding `SootMethod` object from the `view` as shown below: @@ -155,16 +145,19 @@ Once we have a `MethodSignature` that identifies the `main` method of the `Hello ```java Optional opt = view.getMethod(methodSignature); - if(opt.isPresent()){ - SootMethod method = opt.get(); + if(!opt.isPresent()){ + return; } + SootMethod method = opt.get(); + System.out.println(method.getModifiers()); ``` Alternatively, we can also retrieve a `SootMethod` from `SootClass` that contains it. !!! example "Retrieving a SootMethod from a SootClass" ```java - Optional opt = sootClass.getMethod(methodSignature.getSubSignature()); + MethodSubSignature mss = methodSignature.getSubSignature() + Optional opt = sootClass.getMethod(mss); if(opt.isPresent()){ JavaSootMethod method = opt.get(); @@ -178,201 +171,51 @@ Each `SootMethod` contains a Control-Flow Graph (CFG) which is represented via t !!! example "Retrieving the CFG of a SootMethod" ```java - sootMethod.getBody().getStmts(); + StmtGraph graph = sootMethod.getBody().getStmtGraph(); ``` +## Using the StmtGraph -!!! info "Access or Download all of the code used above" - - [BasicSetup.java](https://github.com/secure-software-engineering/soot-reloaded/blob/develop/sootup.examples/src/test/java/sootup/examples/basicSetup/BasicSetup.java) - -## SootUp vs Soot - -Below we show a comparison of the code so far with the same functionality in sootup. - -=== "SootUp" - - ``` java - AnalysisInputLocation inputLocation = - new JavaClassPathAnalysisInputLocation("path2Binary"); - - JavaView view = new JavaView(inputLocation); - - JavaClassType classType = - view.getIdentifierFactory().getClassType("HelloWorld"); - - MethodSignature methodSignature = - view - .getIdentifierFactory() - .getMethodSignature( - "main", classType, "void", - Collections.singletonList("java.lang.String[]")); - - JavaSootClass sootClass = view.getClass(classType).get(); +=== "StmtGraph Stmts" + ```java + for( Stmt stmt : graph.nodes()){ + // pseudo topological order as Stmts would be serialized to a Jimple file. + } - JavaSootMethod sootMethod = sootClass.getMethod(methodSignature.getSubSignature()).get(); - - sootMethod.getBody().getStmts(); + for( Stmt stmt : graph.nodes()){ + // Stmts are unordered! + } ``` +=== "StmtGraph Blocks" + ```java + List> blocks = graph.getBlocks(); + for( BasicBlock block : blocks){ + // e.g. check if its a merge point + if(block.getPredecessors().size() > 1){ + ... + } -=== "Soot" - - ``` java - G.reset(); - String userdir = System.getProperty("user.dir"); - String sootCp = - userdir - + File.separator - + "target" - + File.separator - + "test-classes" - + File.pathSeparator + "lib"+File.separator+"rt.jar"; - - Options.v().set_soot_classpath(sootCp); - Options.v().set_whole_program(true); - Options.v().setPhaseOption("cg.cha", "on"); - Options.v().setPhaseOption("cg", "all-reachable:true"); - Options.v().set_no_bodies_for_excluded(true); - Options.v().set_allow_phantom_refs(true); - Options.v().setPhaseOption("jb", "use-original-names:true"); - Options.v().set_prepend_classpath(false); - - Scene.v().addBasicClass("java.lang.StringBuilder"); - SootClass c = - Scene.v().forceResolve(targetTestClassName, SootClass.BODIES); - if (c != null) { - c.setApplicationClass(); - } - Scene.v().loadNecessaryClasses(); - - SootMethod method; - for (SootClass c : Scene.v().getApplicationClasses()) { - if(c.getName().equals("example.HelloWorld")){ - for (SootMethod m : c.getMethods()) { - if (!m.hasActiveBody()) { - continue; - } - if (m.getName().equals("entryPoint")) { - method = m; - break; - } - } + // e.g. check if its a branching point + if(block.getSuccessors().size() > 1){ + // or use block.getTail() instanceof BranchingStmt + ... + } + + // e.g. check if thrown exceptions would be caught in this method + if(!block.getExceptionalSuccessors().isEmpty()){ + ... } } - - method.getActiveBody().getUnits(); + ``` +=== "StmtGraph DotExport" + ```java + String urlToWebeditor = DotExporter.createUrlToWebeditor(this); + System.out.println(urlToWebeditor); ``` - - - - - - - +!!! info "Access a complete example of the code used above" -# SootUp-Examples -Example code to help getting start with SootUp - - -1) Here we will provide some examples that uses SootUp to provide insights about a Java program. The repository that contains the examples can be found in [https://github.com/soot-oss/SootUp-Examples.git]. -2) There are mainly 5 projects to be considered under SootUp. - a) *BasicSetupExample* - b) *BodyInterceptorExample* - c) *CallGraphExample* - d) *ClassHierarchyExample* - e) *MutatingSootClassExample*
- -3) We have included all the five projects in 5 different branches under SootUp-Examples with detailed explanation about the project. - - a) BasicSetupExample - 1) package sootup.examples; - defines the package name for the Java class. - 2) import statement - defines various classes and interfaces from different packages that the program uses. - 3) public class BasicSetup - declares a public class named 'BasicSetup' which is the main class for this program. - 4) Then we have created a main method which is the entry point of the progrram - 5) Path pathToBinary object pointing to a directory that contains the binary files ie class files to be analyzed and Paths.get is a static method that converts string path to a 'Path' object. - 6) AnalysisInputLocation object specifying where SootUp should look for classes to analyze. - 7) View object is created for the project allowing the retrieal of classes from the specified input location. JavaView is specific implementation of View tailed for Java projects. - 8) The ClassType object is created for the class name 'HelloWorld'. This object represents the type of class to be analyzed. - 9) A MethodSignature object is created for the main method of the HelloWorld class. This signature specifies the method's return type (void) and its parameter types (a single parameter of type String[]). - 10) The if statment checks for the presences of the class 'HelloWorld'. If not it prints "Class not ffound!" and exits the program. - 11) Then it retrieves the SootClass object representing the HelloWorld class, assuming it is present. - 12) view.getMethod(methodSignature); - Attempts to retrieve the specified method from the project. - 13) The if statment after this, checks if the main method is present in the HelloWorld class. If not, it prints "Method not found!" and exits. - 14) Then the next statment retrieves the SootMethod object for the main method and prints its body, which is in Jimple, a simplified version of Java bytecode used by Soot for analysis and transformation. - 15) Then the next if condition checks if the method containts a specific statement called 'Hello World!'. - - b) BodyInterceptor - 1) package sootup.examples; - defines the package name for the Java class. - 2) import statement - defines various classes and interfaces from different packages that the program uses. - 3) public class BodyInterceptor - declares a public class named "BodyInterceptor". - 4) Then we have created a main method in which is the entry point for the code. - 5) Then we have created an AnalysisInputLocation pointing to a directory with class files to be loaded. It specifies that the DeadAssignmentEliminator interceptor should be applied to these classes. - 6) Then created a View that initializes a JavaView with the specified inputLocation, allowing interaction with the classes for analysis. - 7) Then have created a ClassType and MethodSignature which is used for analysis. The signature contains method name, return type and parameters. - 8) Then we check for the existence of the class and method in the given view. - 9) If they exist, a SootClass and SootMethod objects are used to retrieve the same. - 10) Then prints the body of the SootMethod object. - 11) Then we check if the interceptor worked. ie here we check if the DeadAssignmentEliminator interceptor has successfully removed a specific assignment (l1 = 3) from the method's body. It does this by looking through all statements (JAssignStmt) in the method body and checking if the assignment is not present. - 12) Then it prints the result of the interceptor check. - - - c) CallGraphExample - 1) package sootup.examples; - defines the package name for the Java class. - 2) import statement - defines various classes and interfaces from different packages that the program uses. - 3) public class CallgraphExample - declares a public class named "CallGraphExample". - 4) Then we have created a main method in which is the entry point for the code. - 5) List inputLocations creates a list of AnalysisInputLocation objects. These specify where Soot should look for Java class files for analysis. - 6) Then we have provided towo inputLocations.add() - one for the project's class file directory and another for Java's runtime library (rt.jar). - 7) Then we have created a JavaView which is used for analysing the Java program. - 8) Then we have created two ClassType for two classes ie 'A' and 'B'. They are used to create a MethodSignature for a method that will be analysed. - 9) ViewTypeHierarchy - then we have set up a type hierarchy from the provided view and prints the subclasses of class 'A'. - 10) Initializes a CallGraphAlgorithm using the ClassHierarchyAnalysisAlgorithm, which is a method for constructing call graphs. - 11) Then we creates a call graph by initialising the Class Hierarchy Analysis (cha) with the entry method signature. - 12) Prints information about calls from the entry method in the call graph. - - d) ClassHierarchyExample - 1) package sootup.examples; - defines the package name for the Java class. - 2) import statement - defines various classes and interfaces from different packages that the program uses. - 3) public class ClassHierarchy - declares a public class named "ClassHierarchy". - 4) Then we have created a main method in which is the entry point for the code. - 5) Then creates a list of AnalysisInputLocation objects. These specify where Soot should look for Java class files for analysis. Two locations are added: one for the project's binary directory and another for the default Java runtime library (rt.jar). - 6) Initializes a JavaView object with the previously created input locations. - 7) Initializes a ViewTypeHierarchy object using the view. This object will be used to analyze the class hierarchy. - 8) Then we have created two ClassTypes. These lines get JavaClassType objects for classes "A" and "C". These types are used for further hierarchy analysis. - 9) Checks the direct subclasses of class "C". It verifies if all direct subclasses are "D" using two different methods: comparing class names and fully qualified names. - 10) Then prints a message based on whether all direct subtypes of "C" are correctly identified as "D". - 11) Retrieves and checks the superclasses of class "C". It then verifies if these superclasses include class "A" and java.lang.Object, printing a message based on the result. - - e) MutatingSootClassExample - - 1) package sootup.examples; - defines the package name for the Java class. - 2) import statement - defines various classes and interfaces from different packages that the program uses. - 3) public class MutatingSootClass - declares a public class named "MutatingSootClass". - 4) Then we have created a main method in which is the entry point for the code. - 5) First we have created an 'AnalysisInputLocation' which points to a directory which contains the class files to be analysed. - 6) Then we have created a JavaView which allos us to retrievet the classes. - 7) And also created a ClassType to get the class 'HelloWorld' and a method within that class ie main for analysis using MethodSignature. - 8) THen we are checking and retrieving the class and method. - 9) Then we retrives the existing body of the method and prints it. Then we create a new local variable to add it copy to the method body. - 10) Then we are overriding the method body and class. ie this lines creates new sources that overrides teh original method body and class. It replaces the old method in the class with the new method having the modified body. - 11) Prints the modified method body and checks if the new local variable (newLocal) exists in the modified method. Depending on the result, it prints a corresponding message. + Download [BasicSetup.java](https://github.com/secure-software-engineering/soot-reloaded/blob/develop/sootup.examples/src/test/java/sootup/examples/basicSetup/BasicSetup.java) diff --git a/docs/SootUpLogo.svg b/docs/img/SootUpLogo.svg similarity index 100% rename from docs/SootUpLogo.svg rename to docs/img/SootUpLogo.svg diff --git a/docs/icon.svg b/docs/img/icon.svg similarity index 100% rename from docs/icon.svg rename to docs/img/icon.svg diff --git a/docs/index.md b/docs/index.md index cd61f29e954..69638181c58 100644 --- a/docs/index.md +++ b/docs/index.md @@ -32,11 +32,13 @@ The goal is a lighter library that can easily be understood and maintained to be The development of SootUp is financed by generous support from the German Research Foundation (DFG) and the Heinz Nixdorf Institute (HNI). - +
+
[Become a sponsor!](https://github.com/sponsors/soot-oss) + diff --git a/docs/installation.md b/docs/installation.md index 35f840dec4a..239f709f565 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -1,105 +1,83 @@ # Installation -## Using the latest version on the develop branch -visit [SootUp on Jitpack.io](https://jitpack.io/#soot-oss/SootUp/develop-SNAPSHOT) for configuration options of your build tool. -## Using the release -SootUp is available in maven central, you can include it in your project as follows. - -Below we only show how you can add the SootUp modules to your project. It is not necessary to add all the modules as dependency. -Depending on your needs you can import only the modules you need. -Take a look at the [Modules](whatsnew.md#modular-architecture) to learn more about which modules you might need. - -### Maven - - Add the following dependency in the ```pom.xml``` file of your project to include all SootUp modules into your project. - -``` - - - jitpack.io - https://jitpack.io - - - - - - org.soot-oss - sootup.core - {{ git_latest_release }} - - - org.soot-oss - sootup.java.core - {{ git_latest_release }} - - - org.soot-oss - sootup.java.sourcecode - {{ git_latest_release }} - - - org.soot-oss - sootup.java.bytecode - {{ git_latest_release }} - - - org.soot-oss - sootup.jimple.parser - {{ git_latest_release }} - - - org.soot-oss - sootup.callgraph - {{ git_latest_release }} - - - org.soot-oss - sootup.analysis - {{ git_latest_release }} - - -``` -### Gradle - -Add the following dependency in the ```build.gradle``` file of your project to include all SootUp modules into your project. - -``` -repositories { - mavenCentral() - google() - maven { - url "https://jitpack.io" +## Use the latest [develop branch](https://github.com/soot-oss/SootUp/tree/develop) +For configuration options of your build tool please visit [SootUp on Jitpack.io](https://jitpack.io/#soot-oss/SootUp/develop-SNAPSHOT) + + +## Use Releases on Maven Central +The code below shows you how to import all submodules of the SootUp repository. +You can import fewer modules if your use case allows it. + +Add the following dependencies to your ```pom.xml``` / ```build.gradle```. + +=== "Maven" + + ```mvn + + + org.soot-oss + sootup.core + {{ git_latest_release }} + + + org.soot-oss + sootup.java.core + {{ git_latest_release }} + + + org.soot-oss + sootup.java.sourcecode + {{ git_latest_release }} + + + org.soot-oss + sootup.java.bytecode + {{ git_latest_release }} + + + org.soot-oss + sootup.jimple.parser + {{ git_latest_release }} + + + org.soot-oss + sootup.callgraph + {{ git_latest_release }} + + + org.soot-oss + sootup.analysis + {{ git_latest_release }} + + + org.soot-oss + sootup.qilin + {{ git_latest_release }} + + + ``` + +=== "Gradle" + + ```groovy + repositories { + mavenCentral() + google() } -} - -compile "org.soot-oss:sootup.core:{{ git_latest_release }}" -compile "org.soot-oss:sootup.java.core:{{ git_latest_release }}" -compile "org.soot-oss:sootup.java.sourcecode:{{ git_latest_release }}" -compile "org.soot-oss:sootup.java.bytecode:{{ git_latest_release }}" -compile "org.soot-oss:sootup.jimple.parser:{{ git_latest_release }}" -compile "org.soot-oss:sootup.callgraph:{{ git_latest_release }}" -compile "org.soot-oss:sootup.analysis:{{ git_latest_release }}" -``` - -## Building from Source -Build from source if you'd like to get the most recent changes. -You can download the project as a zip file, or clone it using your favorite git client app or the command line: - -``` + + compile "org.soot-oss:sootup.core:{{ git_latest_release }}" + compile "org.soot-oss:sootup.java.core:{{ git_latest_release }}" + compile "org.soot-oss:sootup.java.sourcecode:{{ git_latest_release }}" + compile "org.soot-oss:sootup.java.bytecode:{{ git_latest_release }}" + compile "org.soot-oss:sootup.jimple.parser:{{ git_latest_release }}" + compile "org.soot-oss:sootup.callgraph:{{ git_latest_release }}" + compile "org.soot-oss:sootup.analysis:{{ git_latest_release }}" + compile "org.soot-oss:sootup.qilin:{{ git_latest_release }}" + ``` + +## Build from Source +If you'd like to get the most recent changes, you can build SootUp from source yourself and install it into your local maven repository. +```sh git clone https://github.com/secure-software-engineering/SootUp.git -``` - -SootUp is a maven project. You can import it into your favorite IDE as a maven project. Run maven clean and install tasks using your IDE's maven plugin to set up the project. - -Alternatively, you can execute the following command in the project directory: - -``` mvn install -``` - -Or if you want to skip tests while building: - -``` -mvn -Dskiptests install -``` - +``` \ No newline at end of file diff --git a/docs/jimple-body.md b/docs/jimple-body.md new file mode 100644 index 00000000000..39ed9a06dd3 --- /dev/null +++ b/docs/jimple-body.md @@ -0,0 +1,142 @@ +# Jimple Body +A SootMethod `Body` consists of the `Modifiers` and its `StmtGraph` - SootUps Control Flow Graph Structure. +The StmtGraph models the flow of [Stmts](jimple-stmts.md). + +### Control Flow Graph +- unexceptional flow -> like FallsThroughStmts and BranchingStmts for if,goto etc. +- exceptional flow -> for exceptions, handled by traps. + +### Stmts +Learn more about the types of [Stmts](jimple-stmts.md). + +### Traps +A Trap is a mechanism to model exceptional flow. +A Trap represents the try-catch (finally) construct and therefore defines the type of the caught exception, the try-catch range (from-to) and the actual code that handles the exception (handler). +In serialized(!) Jimple Labels are used to denote from,to and handler Stmts. + +=== "Jimple" + + ```jimple hl_lines="39" + public class target.exercise1.DemoClass extends java.lang.Object + { + public void () + { + target.exercise1.DemoClass this; + this := @this: target.exercise1.DemoClass; + specialinvoke this.()>(); + return; + } + + public void divideExample(int, int) + { + int x, y, $stack4; + java.io.PrintStream $stack5, $stack7; + java.lang.Exception $stack6; + target.exercise1.DemoClass this; + + this := @this: target.exercise1.DemoClass; + x := @parameter0: int; + y := @parameter1: int; + + label1: + $stack5 = ; + $stack4 = x / y; + virtualinvoke $stack5.($stack4); + + label2: + goto label4; + + label3: + $stack6 := @caughtexception; + $stack7 = ; + virtualinvoke $stack7.("Exception caught"); + + label4: + return; + + catch java.lang.Exception from label1 to label2 with label3; + } + } + /* + By calling getTraps() method, we can get the Traip chain. + For the above jimple code, we have the below trap: + Trap : + begin : $stack5 = + end : goto [?= return] + handler: $stack6 := @caughtexception + */ + ``` + +=== "Java" + + ```java + package target.exercise1; + + public class DemoClass { + public void divideExample(int x, int y){ + try { + System.out.println(x / y); + }catch (Exception e){ + System.out.println("Exception caught"); + } + } + } + ``` + +=== "Bytecode" + + ``` + // class version 52.0 (52) + // access flags 0x21 + public class target/exercise1/DemoClass { + + // compiled from: DemoClass.java + + // access flags 0x1 + public ()V + L0 + LINENUMBER 3 L0 + ALOAD 0 + INVOKESPECIAL java/lang/Object. ()V + RETURN + L1 + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 + MAXSTACK = 1 + MAXLOCALS = 1 + + // access flags 0x1 + public divideExample(II)V + TRYCATCHBLOCK L0 L1 L2 java/lang/Exception + L0 + LINENUMBER 6 L0 + GETSTATIC java/lang/System.out : Ljava/io/PrintStream; + ILOAD 1 + ILOAD 2 + IDIV + INVOKEVIRTUAL java/io/PrintStream.println (I)V + L1 + LINENUMBER 9 L1 + GOTO L3 + L2 + LINENUMBER 7 L2 + FRAME SAME1 java/lang/Exception + ASTORE 3 + L4 + LINENUMBER 8 L4 + GETSTATIC java/lang/System.out : Ljava/io/PrintStream; + LDC "Exception caught" + INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V + L3 + LINENUMBER 10 L3 + FRAME SAME + RETURN + L5 + LOCALVARIABLE e Ljava/lang/Exception; L4 L3 3 + LOCALVARIABLE this Land Ttarget/exercise1/DemoClass; L0 L5 0 + LOCALVARIABLE x I L0 L5 1 + LOCALVARIABLE y I L0 L5 2 + MAXSTACK = 3 + MAXLOCALS = 4 + } + ``` diff --git a/docs/jimple-stmts.md b/docs/jimple-stmts.md new file mode 100644 index 00000000000..06bc1c8a233 --- /dev/null +++ b/docs/jimple-stmts.md @@ -0,0 +1,1218 @@ +# Jimple Stmt ("Statement") +[Stmts]{formerly known as Units} represent instructions of the JVM. +Jimple is a 3-address form code so there are max 3 operands used in a ("manipulating") Stmt - i.e. this does not apply to invokes as this is just operand/parameter passing. + +Stmts can be roughly grouped by the amount of successors (in the `StmtGraph` of a `Body` of a `Method`). + +- A `FallsThroughStmt` has always one successor - it basically represents `program counter++`. +- A `BranchingStmt` can have one, two or even n successors. +- All others (neither FallsThrough nor BranchingStmt) have no successors and therefore end the execution of the current method. + +## Branching Stmts +A BranchingStmt's job is to model the jumps or conditional branching flow between Stmts. + +### JGotoStmt +represents unconditional jumps to another Stmt. + +=== "Jimple" + + ```jimple hl_lines="18 22" + public class target.exercise1.DemoClass extends java.lang.Object + { + public void () + { + target.exercise1.DemoClass this; + this := @this: target.exercise1.DemoClass; + specialinvoke this.()>(); + return; + } + public static void sampleMethod() + { + int i; + i = 0; + + label1: + if i >= 5 goto label3; + if i != 3 goto label2; + goto label3; + + label2: + i = i + 1; + goto label1; + + label3: + return; + } + } + /* + Here for statements "goto label3;" and "goto label1;", + we have two instances of JGotoStmt : + "goto[?=return]" and "goto[?=(branch)]". + */ + ``` + +=== "Java" + + ```java + package target.exercise1; + + public class DemoClass { + public static void sampleMethod(){ + label1: + for (int i = 0; i < 5; i++){ + if(i == 3){ + break label1; + } + } + } + } + ``` + +=== "Bytecode" + + ``` + // class version 52.0 (52) + // access flags 0x21 + public class target/exercise1/DemoClass { + + // compiled from: DemoClass.java + + // access flags 0x1 + public ()V + L0 + LINENUMBER 3 L0 + ALOAD 0 + INVOKESPECIAL java/lang/Object. ()V + RETURN + L1 + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 + MAXSTACK = 1 + MAXLOCALS = 1 + + // access flags 0x9 + public static sampleMethod()V + L0 + LINENUMBER 6 L0 + ICONST_0 + ISTORE 0 + L1 + FRAME APPEND [I] + ILOAD 0 + ICONST_5 + IF_ICMPGE L2 + L3 + LINENUMBER 7 L3 + ILOAD 0 + ICONST_3 + IF_ICMPNE L4 + L5 + LINENUMBER 8 L5 + GOTO L2 + L4 + LINENUMBER 6 L4 + FRAME SAME + IINC 0 1 + GOTO L1 + L2 + LINENUMBER 11 L2 + FRAME CHOP 1 + RETURN + LOCALVARIABLE i I L1 L2 0 + MAXSTACK = 2 + MAXLOCALS = 1 + } + ``` + +### JIfStmt +For conditional jumps depending on the result of the conditional expression `AbstractConditionExpr` which needs to have boolean result. +If the conditional expression is false, the next Stmt is the successor as the JIFStmt is also a `FallsthroughStmt`. +Therefore, the JIfStmt has two successor Stmt's. + +=== "Jimple" + + ```jimple hl_lines="19" + public class target.exercise1.DemoClass extends java.lang.Object + { + public void () + { + target.exercise1.DemoClass this; + this := @this: target.exercise1.DemoClass; + specialinvoke this.()>(); + return; + } + + public static void sampleMethod(int) + { + int x, $stack1; + java.io.PrintStream $stack2, $stack3; + + x := @parameter0: int; + + $stack1 = x % 2; + if $stack1 != 0 goto label1; + + $stack3 = ; + virtualinvoke $stack3.("Even"); + goto label2; + + label1: + $stack2 = ; + virtualinvoke $stack2.("Odd"); + + label2: + return; + } + } + /* + For statement "if $stack1 != 0 goto label1;", + we have an instance of JIfStmt : + "if $stack1 != 0 goto $stack2 + = ". + */ + ``` + +=== "Java" + + ```java + package target.exercise1; + + public class DemoClass { + public static void sampleMethod(int x){ + if(x % 2 == 0){ + System.out.println("Even"); + }else{ + System.out.println("Odd"); + } + } + } + ``` + +=== "Bytecode" + + ``` + // class version 52.0 (52) + // access flags 0x21 + public class target/exercise1/DemoClass { + + // compiled from: DemoClass.java + + // access flags 0x1 + public ()V + L0 + LINENUMBER 3 L0 + ALOAD 0 + INVOKESPECIAL java/lang/Object. ()V + RETURN + L1 + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 + MAXSTACK = 1 + MAXLOCALS = 1 + + // access flags 0x9 + public static sampleMethod(I)V + L0 + LINENUMBER 5 L0 + ILOAD 0 + ICONST_2 + IREM + IFNE L1 + L2 + LINENUMBER 6 L2 + GETSTATIC java/lang/System.out : Ljava/io/PrintStream; + LDC "Even" + INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V + GOTO L3 + L1 + LINENUMBER 8 L1 + FRAME SAME + GETSTATIC java/lang/System.out : Ljava/io/PrintStream; + LDC "Odd" + INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V + L3 + LINENUMBER 10 L3 + FRAME SAME + RETURN + L4 + LOCALVARIABLE x I L0 L4 0 + MAXSTACK = 2 + MAXLOCALS = 1 + } + ``` + +### JSwitchStmt +for conditional flow that behaves like a switch-case. It has #numberOfCaseLabels+1 (for default) successor Stmt's. + +=== "Jimple" + + ```jimple hl_lines="20-25" + public class target.exercise1.DemoClass extends java.lang.Object + { + public void () + { + target.exercise1.DemoClass this; + this := @this: target.exercise1.DemoClass; + specialinvoke this.()>(); + return; + } + + public void switchExample(int) + { + int x; + java.io.PrintStream $stack2, $stack3, $stack4; + target.exercise1.DemoClass this; + + this := @this: target.exercise1.DemoClass; + x := @parameter0: int; + + lookupswitch(x) + { + case 1: goto label1; + case 2: goto label2; + default: goto label3; + }; + + label1: + $stack3 = ; + virtualinvoke $stack3.("Input 1"); + goto label4; + + label2: + $stack2 = ; + virtualinvoke $stack2.("Input 2"); + goto label4; + + label3: + $stack4 = ; + virtualinvoke $stack4.("Input more than 2"); + + label4: + return; + } + } + /* + Here for below statement: + lookupswitch(x) + { + case 1: goto label1; + case 2: goto label2; + default: goto label3; + }; + + we have an instance of JLookupSwitchStmt : + lookupswitch(x) + { + case 1: goto $stack3 + = ; + case 2: goto $stack2 + = ; + default: goto $stack4 + = ; + } + */ + ``` + +=== "Java" + + ```java + package target.exercise1; + + public class DemoClass { + public void switchExample(int x){ + switch (x){ + case 1: + System.out.println("Input 1"); + break; + + case 2: + System.out.println("Input 2"); + break; + + default: + System.out.println("Input more than 2"); + break; + + } + } + } + ``` + +=== "Bytecode" + + ``` + // class version 52.0 (52) + // access flags 0x21 + public class target/exercise1/DemoClass { + + // compiled from: DemoClass.java + + // access flags 0x1 + public ()V + L0 + LINENUMBER 3 L0 + ALOAD 0 + INVOKESPECIAL java/lang/Object. ()V + RETURN + L1 + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 + MAXSTACK = 1 + MAXLOCALS = 1 + + // access flags 0x1 + public switchExample(I)V + L0 + LINENUMBER 5 L0 + ILOAD 1 + LOOKUPSWITCH + 1: L1 + 2: L2 + default: L3 + L1 + LINENUMBER 7 L1 + FRAME SAME + GETSTATIC java/lang/System.out : Ljava/io/PrintStream; + LDC "Input 1" + INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V + L4 + LINENUMBER 8 L4 + GOTO L5 + L2 + LINENUMBER 11 L2 + FRAME SAME + GETSTATIC java/lang/System.out : Ljava/io/PrintStream; + LDC "Input 2" + INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V + L6 + LINENUMBER 12 L6 + GOTO L5 + L3 + LINENUMBER 15 L3 + FRAME SAME + GETSTATIC java/lang/System.out : Ljava/io/PrintStream; + LDC "Input more than 2" + INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V + L5 + LINENUMBER 19 L5 + FRAME SAME + RETURN + L7 + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L7 0 + LOCALVARIABLE x I L0 L7 1 + MAXSTACK = 2 + MAXLOCALS = 2 + } + ``` + +## FallsThrough Stmts +The execution of a FallsthroughStmt goes on with the following Stmt (if no exception was thrown). + +### JInvokeStmt +transfers the control flow to another method until the called method returns. + +=== "Jimple" + + ```jimple hl_lines="7 20 22 24 26" + public class target.exercise1.DemoClass extends java.lang.Object + { + public void () + { + target.exercise1.DemoClass this; + this := @this: target.exercise1.DemoClass; + specialinvoke this.()>(); + return; + } + + public void print(int) + { + target.exercise1.DemoClass this; + int x, a; + java.io.PrintStream $stack4, $stack6; + + this := @this: target.exercise1.DemoClass; + x := @parameter0: int; + + a = virtualinvoke this.(x); + $stack4 = ; + virtualinvoke $stack4.(a); + + a = virtualinvoke this.(a); + $stack6 = ; + virtualinvoke $stack6.(a); + + return; + } + + public int increment(int) + { + int x, $stack2; + target.exercise1.DemoClass this; + + this := @this: target.exercise1.DemoClass; + x := @parameter0: int; + + $stack2 = x + 1; + return $stack2; + } + } + /* + "specialinvoke this.()>()", + "virtualinvoke this.(x)", + "virtualinvoke this.(a)" + are JInvokeStmts. + */ + ``` + +=== "Java" + + ```java + package target.exercise1; + + public class DemoClass { + public void print(int x){ + int a = increment(x); + System.out.println(a); + a = increment(a); + System.out.println(a); + } + public int increment(int x){ + return x + 1; + } + } + ``` + +=== "Bytecode" + + ``` + // class version 52.0 (52) + // access flags 0x21 + public class target/exercise1/DemoClass { + + // compiled from: DemoClass.java + + // access flags 0x1 + public ()V + L0 + LINENUMBER 3 L0 + ALOAD 0 + INVOKESPECIAL java/lang/Object. ()V + RETURN + L1 + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 + MAXSTACK = 1 + MAXLOCALS = 1 + + // access flags 0x1 + public print(I)V + L0 + LINENUMBER 5 L0 + ALOAD 0 + ILOAD 1 + INVOKEVIRTUAL target/exercise1/DemoClass.increment (I)I + ISTORE 2 + L1 + LINENUMBER 6 L1 + GETSTATIC java/lang/System.out : Ljava/io/PrintStream; + ILOAD 2 + INVOKEVIRTUAL java/io/PrintStream.println (I)V + L2 + LINENUMBER 7 L2 + ALOAD 0 + ILOAD 2 + INVOKEVIRTUAL target/exercise1/DemoClass.increment (I)I + ISTORE 2 + L3 + LINENUMBER 8 L3 + GETSTATIC java/lang/System.out : Ljava/io/PrintStream; + ILOAD 2 + INVOKEVIRTUAL java/io/PrintStream.println (I)V + L4 + LINENUMBER 9 L4 + RETURN + L5 + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L5 0 + LOCALVARIABLE x I L0 L5 1 + LOCALVARIABLE a I L1 L5 2 + MAXSTACK = 2 + MAXLOCALS = 3 + + // access flags 0x1 + public increment(I)I + L0 + LINENUMBER 11 L0 + ILOAD 1 + ICONST_1 + IADD + IRETURN + L1 + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 + LOCALVARIABLE x I L0 L1 1 + MAXSTACK = 2 + MAXLOCALS = 2 + } + ``` + + +### JAssignStmt +assigns a Value from the right hand-side to the left hand-side. +Left hand-side of an assignment can be a Local referencing a variable (i.e. a Local) or a FieldRef referencing a Field. +Right hand-side of an assignment can be an expression (Expr), a Local, a FieldRef or a Constant. + +=== "Jimple" + + ```jimple hl_lines="8 19-22" + public class target.exercise1.DemoClass extends java.lang.Object + { + public void () + { + target.exercise1.DemoClass this; + this := @this: target.exercise1.DemoClass; + specialinvoke this.()>(); + this. = 0; + return; + } + + public int updateCounter() + { + target.exercise1.DemoClass this; + int $stack1, $stack2, $stack3; + + this := @this: target.exercise1.DemoClass; + + $stack1 = this.; + $stack2 = $stack1 + 1; + this. = $stack2; + $stack3 = this.; + + return $stack3; + } + } + /* + "this. = 0", + "$stack1 = this.", + "$stack2 = $stack1 + 1" + "this. = $stack2" + "$stack3 = this." + are JAssignStmts. + */ + ``` + +=== "Java" + + ```java + package target.exercise1; + + public class DemoClass { + private int counter = 0; + public int updateCounter(){ + counter = counter + 1; + return counter; + } + } + ``` + +=== "Bytecode" + + ``` + // class version 52.0 (52) + // access flags 0x21 + public class target/exercise1/DemoClass { + + // compiled from: DemoClass.java + + // access flags 0x2 + private I counter + + // access flags 0x1 + public ()V + L0 + LINENUMBER 3 L0 + ALOAD 0 + INVOKESPECIAL java/lang/Object. ()V + L1 + LINENUMBER 4 L1 + ALOAD 0 + ICONST_0 + PUTFIELD target/exercise1/DemoClass.counter : I + RETURN + L2 + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L2 0 + MAXSTACK = 2 + MAXLOCALS = 1 + + // access flags 0x1 + public updateCounter()I + L0 + LINENUMBER 6 L0 + ALOAD 0 + ALOAD 0 + GETFIELD target/exercise1/DemoClass.counter : I + ICONST_1 + IADD + PUTFIELD target/exercise1/DemoClass.counter : I + L1 + LINENUMBER 7 L1 + ALOAD 0 + GETFIELD target/exercise1/DemoClass.counter : I + IRETURN + L2 + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L2 0 + MAXSTACK = 3 + MAXLOCALS = 1 + } + ``` + + +### JIdentityStmt +is similar to the `JAssignStmt` and but handles assignments of `IdentityRef`s to make implicit assignments explicit into the `StmtGraph`. + +- Assigns parameters to a `Local` via `JParameterRef` like `@parameter0: int` refering to the first argument of the method (which is of Type int in this case). +- Assigns exceptions to a `Local` via `JCaughtExceptionRef` like `@caughtexception: java.lang.NullpointerException` +- Assigns the `this` Variable to a `Local` via a `JThisRef` + +=== "Jimple" + + ```jimple hl_lines="16 17" + public class target.exercise1.DemoClass extends java.lang.Object + { + public void () + { + target.exercise1.DemoClass this; + this := @this: target.exercise1.DemoClass; + specialinvoke this.()>(); + return; + } + + public void DemoClass(int) + { + target.exercise1.DemoClass this; + int counter; + + this := @this: target.exercise1.DemoClass; + counter := @parameter0: int; + this. = counter; + return; + } + } + /* + "this := @this: target.exercise1.DemoClass" and + "counter := @parameter0: int" are JIdentityStmts + */ + ``` + +=== "Java" + + ```java + package target.exercise1; + + public class DemoClass { + private int counter; + public void DemoClass(int counter){ + this.counter = counter; + } + } + ``` + +=== "Bytecode" + + ``` + // class version 52.0 (52) + // access flags 0x21 + public class target/exercise1/DemoClass { + + // compiled from: DemoClass.java + + // access flags 0x2 + private I counter + + // access flags 0x1 + public ()V + L0 + LINENUMBER 3 L0 + ALOAD 0 + INVOKESPECIAL java/lang/Object. ()V + RETURN + L1 + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 + MAXSTACK = 1 + MAXLOCALS = 1 + + // access flags 0x1 + public DemoClass(I)V + L0 + LINENUMBER 6 L0 + ALOAD 0 + ILOAD 1 + PUTFIELD target/exercise1/DemoClass.counter : I + L1 + LINENUMBER 7 L1 + RETURN + L2 + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L2 0 + LOCALVARIABLE counter I L0 L2 1 + MAXSTACK = 2 + MAXLOCALS = 2 + } + ``` + + +###JEnterMonitorStmt & JExitMonitorStmt +marks synchronized blocks of code from JEnterMonitorStmt to JExitMonitorStmt. + +=== "Jimple" + + ```jimple hl_lines="20 27 35" + public class target.exercise1.DemoClass extends java.lang.Object + { + public void () + { + target.exercise1.DemoClass this; + this := @this: target.exercise1.DemoClass; + specialinvoke this.()>(); + this. = 0; + return; + } + + public int updateCounter() + { + target.exercise1.DemoClass this; + int $stack4, $stack5, $stack7; + java.lang.Throwable $stack8; + + this := @this: target.exercise1.DemoClass; + + entermonitor this; + + label1: + $stack4 = this.; + $stack5 = $stack4 + 1; + this. = $stack5; + + exitmonitor this; + + label2: + goto label5; + + label3: + $stack8 := @caughtexception; + + exitmonitor this; + + label4: + throw $stack8; + + label5: + $stack7 = this.; + return $stack7; + + catch java.lang.Throwable from label1 to label2 with label3; + catch java.lang.Throwable from label3 to label4 with label3; + } + } + /* + "entermonitor this" is JEnterMonitorStmt. + "exitmonitor this" is JExitMonitorStmt. + */ + ``` + +=== "Java" + + ```java + package target.exercise1; + + public class DemoClass { + private int counter = 0; + public int updateCounter(){ + synchronized (this) { + counter = counter + 1; + } + return counter; + } + } + ``` + +=== "Bytecode" + + ``` + // class version 52.0 (52) + // access flags 0x21 + public class target/exercise1/DemoClass { + + // compiled from: DemoClass.java + + // access flags 0x2 + private I counter + + // access flags 0x1 + public ()V + L0 + LINENUMBER 3 L0 + ALOAD 0 + INVOKESPECIAL java/lang/Object. ()V + L1 + LINENUMBER 4 L1 + ALOAD 0 + ICONST_0 + PUTFIELD target/exercise1/DemoClass.counter : I + RETURN + L2 + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L2 0 + MAXSTACK = 2 + MAXLOCALS = 1 + + // access flags 0x1 + public updateCounter()I + TRYCATCHBLOCK L0 L1 L2 null + TRYCATCHBLOCK L2 L3 L2 null + L4 + LINENUMBER 6 L4 + ALOAD 0 + DUP + ASTORE 1 + MONITORENTER + L0 + LINENUMBER 7 L0 + ALOAD 0 + ALOAD 0 + GETFIELD target/exercise1/DemoClass.counter : I + ICONST_1 + IADD + PUTFIELD target/exercise1/DemoClass.counter : I + L5 + LINENUMBER 8 L5 + ALOAD 1 + MONITOREXIT + L1 + GOTO L6 + L2 + FRAME FULL [target/exercise1/DemoClass java/lang/Object] + [java/lang/Throwable] + ASTORE 2 + ALOAD 1 + MONITOREXIT + L3 + ALOAD 2 + ATHROW + L6 + LINENUMBER 9 L6 + FRAME CHOP 1 + ALOAD 0 + GETFIELD target/exercise1/DemoClass.counter : I + IRETURN + L7 + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L4 L7 0 + MAXSTACK = 3 + MAXLOCALS = 3 + } + ``` + +### JRetStmt +// TODO: [java 1.6 spec](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.ret) + +### JBreakpointStmt +models a Breakpoint set by a Debugger. Therefore, not really relevant for static analyses but useful for code generation. + +## Other Stmts + +### JReturnStmt & JReturnVoidStmt +They end the execution/flow inside the current method and return (a value) to its caller. + +=== "Jimple" + + ```jimple hl_lines="20 32" + public class target.exercise1.DemoClass extends java.lang.Object + { + public void () + { + target.exercise1.DemoClass this; + this := @this: target.exercise1.DemoClass; + specialinvoke this.()>(); + return; + } + + public int increment(int) + { + int x, $stack2; + target.exercise1.DemoClass this; + + this := @this: target.exercise1.DemoClass; + x := @parameter0: int; + + $stack2 = x + 1; + return $stack2; + } + + public void print() + { + java.io.PrintStream $stack1; + target.exercise1.DemoClass this; + + this := @this: target.exercise1.DemoClass; + $stack1 = ; + virtualinvoke $stack1.("Inside method print"); + return; + } + } + /* + "return $stack2" is JReturnStmt. + "return" is JReturnVoidStmt. + */ + ``` + +=== "Java" + + ```java + package target.exercise1; + + public class DemoClass { + public int increment(int x){ + return x + 1; + } + public void print(){ + System.out.println("Inside method print"); + } + } + ``` + +=== "Bytecode" + + ``` + // class version 52.0 (52) + // access flags 0x21 + public class target/exercise1/DemoClass { + + // compiled from: DemoClass.java + + // access flags 0x1 + public ()V + L0 + LINENUMBER 3 L0 + ALOAD 0 + INVOKESPECIAL java/lang/Object. ()V + RETURN + L1 + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 + MAXSTACK = 1 + MAXLOCALS = 1 + + // access flags 0x1 + public increment(I)I + L0 + LINENUMBER 5 L0 + ILOAD 1 + ICONST_1 + IADD + IRETURN + L1 + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 + LOCALVARIABLE x I L0 L1 1 + MAXSTACK = 2 + MAXLOCALS = 2 + + // access flags 0x1 + public print()V + L0 + LINENUMBER 8 L0 + GETSTATIC java/lang/System.out : Ljava/io/PrintStream; + LDC "Inside method print" + INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V + L1 + LINENUMBER 9 L1 + RETURN + L2 + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L2 0 + MAXSTACK = 2 + MAXLOCALS = 1 + } + ``` + + +### JThrowStmt +Ends the execution inside the current Method if the thrown exception is not caught by a Trap, which redirects the execution to an exceptionhandler. + + +=== "Jimple" + + ```jimple hl_lines="29" + public class target.exercise1.DemoClass extends java.lang.Object + { + public void () + { + target.exercise1.DemoClass this; + this := @this: target.exercise1.DemoClass; + specialinvoke this.()>(); + return; + } + + public void divideExample(int, int) + { + int y, x, $stack6; + java.lang.StringBuilder $stack3, $stack5, $stack7; + java.io.PrintStream $stack4; + java.lang.String $stack8; + java.lang.RuntimeException $stack9; + target.exercise1.DemoClass this; + + this := @this: target.exercise1.DemoClass; + x := @parameter0: int; + y := @parameter1: int; + + if y != 0 goto label1; + + $stack9 = new java.lang.RuntimeException; + specialinvoke $stack9.(java.lang.String)>("Divide by zero error"); + throw $stack9; + + label1: + $stack4 = ; + $stack3 = new java.lang.StringBuilder; + specialinvoke $stack3.()>(); + + $stack5 = virtualinvoke $stack3.("Divide result : "); + $stack6 = x / y; + $stack7 = virtualinvoke $stack5.($stack6); + $stack8 = virtualinvoke $stack7.(); + + virtualinvoke $stack4.($stack8); + return; + } + } + /* + "throw $stack9" is JThrowStmt. + */ + ``` + +=== "Java" + + ```java + package target.exercise1; + + public class DemoClass { + public void divideExample(int x, int y){ + if(y == 0){ + throw new RuntimeException("Divide by zero error"); + } + System.out.println("Divide result : " + x / y); + } + } + ``` + +=== "Bytecode" + + ``` + // class version 52.0 (52) + // access flags 0x21 + public class target/exercise1/DemoClass { + + // compiled from: DemoClass.java + + // access flags 0x1 + public ()V + L0 + LINENUMBER 3 L0 + ALOAD 0 + INVOKESPECIAL java/lang/Object. ()V + RETURN + L1 + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 + MAXSTACK = 1 + MAXLOCALS = 1 + + // access flags 0x1 + public divideExample(II)V + L0 + LINENUMBER 5 L0 + ILOAD 2 + IFNE L1 + L2 + LINENUMBER 6 L2 + NEW java/lang/RuntimeException + DUP + LDC "Divide by zero error" + INVOKESPECIAL java/lang/RuntimeException. + (Ljava/lang/String;)V + ATHROW + L1 + LINENUMBER 8 L1 + FRAME SAME + GETSTATIC java/lang/System.out : Ljava/io/PrintStream; + NEW java/lang/StringBuilder + DUP + INVOKESPECIAL java/lang/StringBuilder. ()V + LDC "Divide result : " + INVOKEVIRTUAL java/lang/StringBuilder.append + (Ljava/lang/String;)Ljava/lang/StringBuilder; + ILOAD 1 + ILOAD 2 + IDIV + INVOKEVIRTUAL java/lang/StringBuilder.append + (I)Ljava/lang/StringBuilder; + INVOKEVIRTUAL java/lang/StringBuilder.toString + ()Ljava/lang/String; + INVOKEVIRTUAL java/io/PrintStream.println + (Ljava/lang/String;)V + L3 + LINENUMBER 9 L3 + RETURN + L4 + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L4 0 + LOCALVARIABLE x I L0 L4 1 + LOCALVARIABLE y I L0 L4 2 + MAXSTACK = 4 + MAXLOCALS = 3 + } + ``` + +## Good to know +A lot of the SootUp APIs return the `Stmt` Interface. To determine and handle its subtypes you can make use of instanceof. +=== "instanceOf & If-Else" + ```java + + List stmts = ... ; + for( Stmt stms : stmts ){ + if(stmt instanceof JAssignStmt){ + // found a JAssignStmt + Value rhsOp = ((JAssignStmt) stmt).getRightOp(); + ... + }else if(stmt instanceof JInvokeStmt){ + // found a JInvokeStmt + JInvokeStmt ivkStmt = ((JInvokeStmt) stmt); + MethodSignature rhsOp = ivkStmt.getInvokeExpr().getMethodSignature(); + ... + }else ... + } + + ``` + +But this could escalate to a huge if-else-tree - almost a forest. To mitigate such scenario you can implement a subclass of `AbstractStmtVisitor`. +Just subclass the methods to the respective Stmts you need to handle. This is visitor acts like a switch-case, implemented via two dynamic calls. +=== "StmtVisitor" + ```java + + List stmts = ...; + AbstractStmtVisitor visitor = new AbstractStmtVisitor() { + private int ifStmtsCounter = 0; + @Override + public void caseIfStmt(@Nonnull JIfStmt stmt) { + ifStmtsCounter++; + setResult(ifStmtCounter); + } + }; + + for( Stmt stms : stmts ){ + stmt.accept(visitor); + } + + int amountOfIfStmts = visitor.getResult(); + ``` + + Sidenote: Of course its possible can create a subclass instead of an anonymous class. \ No newline at end of file diff --git a/docs/jimple-types.md b/docs/jimple-types.md new file mode 100644 index 00000000000..1cfcff8f327 --- /dev/null +++ b/docs/jimple-types.md @@ -0,0 +1,541 @@ +# Jimple Types +represents primary types i.e. non-reference types and non-void + +### PrimaryType + +- `BooleanType` +- `ByteType` +- `CharType` +- `ShortType` +- `IntType` +- `LongType` +- `DoubleType` +- `FloatType` + +=== "Jimple" + + ```jimple hl_lines="14-21" + public class target.exercise1.DemoClass extends java.lang.Object + { + public void () + { + target.exercise1.DemoClass this; + this := @this: target.exercise1.DemoClass; + specialinvoke this.()>(); + return; + } + + + public void display() + { + java.io.PrintStream $stack11, $stack13, $stack15, + $stack17, $stack19, $stack21, $stack23, $stack25; + int $stack12, $stack14, $stack16, $stack18; + long $stack20; + double $stack22; + float $stack24; + target.exercise1.DemoClass this; + boolean $stack26; + + this := @this: target.exercise1.DemoClass; + + $stack11 = ; + + goto label1; + + label1: + $stack26 = 0; + virtualinvoke $stack11.($stack26); + + $stack13 = ; + $stack12 = 127 - 1; + virtualinvoke $stack13.($stack12); + + $stack15 = ; + $stack14 = 97 + 1; + virtualinvoke $stack15.($stack14); + + $stack17 = ; + $stack16 = 1123 + 1; + virtualinvoke $stack17.($stack16); + + $stack19 = ; + $stack18 = 123456 + 1; + virtualinvoke $stack19.($stack18); + + $stack21 = ; + $stack20 = 10L + 1L; + virtualinvoke $stack21.($stack20); + + $stack23 = ; + $stack22 = 10.1 + 1.0; + virtualinvoke $stack23.($stack22); + + $stack25 = ; + $stack24 = 10.1F + 1.0F; + virtualinvoke $stack25.($stack24); + + return; + } + } + /* + The JimpleLocal $stack12, $stack14, $stack16, $stack18 are of IntType. + Similarly, $stack20 is of LongType, $stack22 is of DoubleType and so on. + */ + ``` + +=== "Java" + + ```java + package target.exercise1; + + public class DemoClass { + public void display(){ + boolean varBoolean = true; + byte varByte = 127; + char varChar = 'a'; + short varShort = 1123; + int varInt = 123456; + long varLong = 10L; + double varDouble = 10.10; + float varFloat = 10.10f; + + System.out.println(!varBoolean); + System.out.println(varByte-1); + System.out.println(varChar+1); + System.out.println(varShort+1); + System.out.println(varInt+1); + System.out.println(varLong+1); + System.out.println(varDouble+1); + System.out.println(varFloat+1); + + } + } + ``` + +=== "Bytecode" + + ``` + // class version 52.0 (52) + // access flags 0x21 + public class target/exercise1/DemoClass { + + // compiled from: DemoClass.java + + // access flags 0x1 + public ()V + L0 + LINENUMBER 3 L0 + ALOAD 0 + INVOKESPECIAL java/lang/Object. ()V + RETURN + L1 + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 + MAXSTACK = 1 + MAXLOCALS = 1 + + // access flags 0x1 + public display()V + L0 + LINENUMBER 5 L0 + ICONST_1 + ISTORE 1 + L1 + LINENUMBER 6 L1 + BIPUSH 127 + ISTORE 2 + L2 + LINENUMBER 7 L2 + BIPUSH 97 + ISTORE 3 + L3 + LINENUMBER 8 L3 + SIPUSH 1123 + ISTORE 4 + L4 + LINENUMBER 9 L4 + LDC 123456 + ISTORE 5 + L5 + LINENUMBER 10 L5 + LDC 10 + LSTORE 6 + L6 + LINENUMBER 11 L6 + LDC 10.1 + DSTORE 8 + L7 + LINENUMBER 12 L7 + LDC 10.1 + FSTORE 10 + L8 + LINENUMBER 14 L8 + GETSTATIC java/lang/System.out : Ljava/io/PrintStream; + ILOAD 1 + IFNE L9 + ICONST_1 + GOTO L10 + L9 + FRAME FULL [target/exercise1/DemoClass I I I I I J D F] + [java/io/PrintStream] + ICONST_0 + L10 + FRAME FULL [target/exercise1/DemoClass I I I I I J D F] + [java/io/PrintStream I] + INVOKEVIRTUAL java/io/PrintStream.println (Z)V + L11 + LINENUMBER 15 L11 + GETSTATIC java/lang/System.out : Ljava/io/PrintStream; + ILOAD 2 + ICONST_1 + ISUB + INVOKEVIRTUAL java/io/PrintStream.println (I)V + L12 + LINENUMBER 16 L12 + GETSTATIC java/lang/System.out : Ljava/io/PrintStream; + ILOAD 3 + ICONST_1 + IADD + INVOKEVIRTUAL java/io/PrintStream.println (I)V + L13 + LINENUMBER 17 L13 + GETSTATIC java/lang/System.out : Ljava/io/PrintStream; + ILOAD 4 + ICONST_1 + IADD + INVOKEVIRTUAL java/io/PrintStream.println (I)V + L14 + LINENUMBER 18 L14 + GETSTATIC java/lang/System.out : Ljava/io/PrintStream; + ILOAD 5 + ICONST_1 + IADD + INVOKEVIRTUAL java/io/PrintStream.println (I)V + L15 + LINENUMBER 19 L15 + GETSTATIC java/lang/System.out : Ljava/io/PrintStream; + LLOAD 6 + LCONST_1 + LADD + INVOKEVIRTUAL java/io/PrintStream.println (J)V + L16 + LINENUMBER 20 L16 + GETSTATIC java/lang/System.out : Ljava/io/PrintStream; + DLOAD 8 + DCONST_1 + DADD + INVOKEVIRTUAL java/io/PrintStream.println (D)V + L17 + LINENUMBER 21 L17 + GETSTATIC java/lang/System.out : Ljava/io/PrintStream; + FLOAD 10 + FCONST_1 + FADD + INVOKEVIRTUAL java/io/PrintStream.println (F)V + L18 + LINENUMBER 23 L18 + RETURN + L19 + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L19 0 + LOCALVARIABLE varBoolean Z L1 L19 1 + LOCALVARIABLE varByte B L2 L19 2 + LOCALVARIABLE varChar C L3 L19 3 + LOCALVARIABLE varShort S L4 L19 4 + LOCALVARIABLE varInt I L5 L19 5 + LOCALVARIABLE varLong J L6 L19 6 + LOCALVARIABLE varDouble D L7 L19 8 + LOCALVARIABLE varFloat F L8 L19 10 + MAXSTACK = 5 + MAXLOCALS = 11 + } + ``` + + +### ReferenceType +- `(Java)ClassType` - represents the type of a Class. +- `ArrayType` - represents an array. +- `NullType` - assignable to one of the other ReferenceTypes. + +=== "Jimple" + + ```jimple hl_lines="21 24 26" + public class target.exercise1.DemoClass extends java.lang.Object + { + public void () + { + target.exercise1.DemoClass this; + this := @this: target.exercise1.DemoClass; + specialinvoke this.()>(); + return; + } + + public target.exercise1.DemoClass getObject(target.exercise1.DemoClass) + { + target.exercise1.DemoClass obj, this; + this := @this: target.exercise1.DemoClass; + obj := @parameter0: target.exercise1.DemoClass; + return obj; + } + + public void compute(boolean) + { + int[] b; + java.io.PrintStream $stack5, $stack6; + boolean check; + target.exercise1.DemoClass this; + int i; + null_type r0; + java.lang.NullPointerException soot0; + this := @this: target.exercise1.DemoClass; + check := @parameter0: boolean; + b = newarray (int)[5]; + i = 0; + + label1: + if i >= 5 goto label3; + if check == 0 goto label2; + r0 = (null_type) i; + soot0 = new java.lang.NullPointerException; + specialinvoke soot0.(java.lang.String)> + ("This statement would have triggered an Exception: a[i#1] = r0"); + throw soot0; + + label2: + b[i] = i; + i = i + 1; + goto label1; + + label3: + $stack5 = ; + virtualinvoke $stack5.(b); + $stack6 = ; + virtualinvoke $stack6.(null); + return; + } + } + /* + The Local b is of ArrayType, + and Local r0 is of NullType. + */ + ``` + +=== "Java" + + ```java + package target.exercise1; + + public class DemoClass { + + public DemoClass getObject(DemoClass obj){ + return obj; + } + + public void compute(boolean check){ + int a[] = null; + int b[] = new int[5]; + for (int i = 0; i < 5; i++) { + if(check){ + a[i] = i; + } + b[i] = i; + } + System.out.println(b); + System.out.println(a); + } + } + ``` + +=== "Bytecode" + + ``` + // class version 52.0 (52) + // access flags 0x21 + public class target/exercise1/DemoClass { + + // compiled from: DemoClass.java + + // access flags 0x1 + public ()V + L0 + LINENUMBER 3 L0 + ALOAD 0 + INVOKESPECIAL java/lang/Object. ()V + RETURN + L1 + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 + MAXSTACK = 1 + MAXLOCALS = 1 + + // access flags 0x1 + public getObject(Ltarget/exercise1/DemoClass;)Ltarget/exercise1/DemoClass; + L0 + LINENUMBER 6 L0 + ALOAD 1 + ARETURN + L1 + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 + LOCALVARIABLE obj Ltarget/exercise1/DemoClass; L0 L1 1 + MAXSTACK = 1 + MAXLOCALS = 2 + + // access flags 0x1 + public compute(Z)V + L0 + LINENUMBER 10 L0 + ACONST_NULL + ASTORE 2 + L1 + LINENUMBER 11 L1 + ICONST_5 + NEWARRAY T_INT + ASTORE 3 + L2 + LINENUMBER 12 L2 + ICONST_0 + ISTORE 4 + L3 + FRAME APPEND [[I [I I] + ILOAD 4 + ICONST_5 + IF_ICMPGE L4 + L5 + LINENUMBER 13 L5 + ILOAD 1 + IFEQ L6 + L7 + LINENUMBER 14 L7 + ALOAD 2 + ILOAD 4 + ILOAD 4 + IASTORE + L6 + LINENUMBER 16 L6 + FRAME SAME + ALOAD 3 + ILOAD 4 + ILOAD 4 + IASTORE + L8 + LINENUMBER 12 L8 + IINC 4 1 + GOTO L3 + L4 + LINENUMBER 18 L4 + FRAME CHOP 1 + GETSTATIC java/lang/System.out : Ljava/io/PrintStream; + ALOAD 3 + INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V + L9 + LINENUMBER 19 L9 + GETSTATIC java/lang/System.out : Ljava/io/PrintStream; + ALOAD 2 + INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V + L10 + LINENUMBER 20 L10 + RETURN + L11 + LOCALVARIABLE i I L3 L4 4 + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L11 0 + LOCALVARIABLE check Z L0 L11 1 + LOCALVARIABLE a [I L1 L11 2 + LOCALVARIABLE b [I L2 L11 3 + MAXSTACK = 3 + MAXLOCALS = 5 + } + ``` + + +### VoidType +Used as a possible return type of a method. + +=== "Jimple" + + ```jimple hl_lines="11" + public class target.exercise1.DemoClass extends java.lang.Object + { + public void () + { + target.exercise1.DemoClass this; + this := @this: target.exercise1.DemoClass; + specialinvoke this.()>(); + return; + } + + public void voidMethod() + { + java.io.PrintStream $stack1; + target.exercise1.DemoClass this; + this := @this: target.exercise1.DemoClass; + $stack1 = ; + virtualinvoke $stack1.("In voidMethod()."); + return; + } + } + /* + For the SootMethod - , + returnType is instance of VoidType. + */ + ``` + +=== "Java" + + ```java + package target.exercise1; + + public class DemoClass { + public void voidMethod(){ + System.out.println("In voidMethod()."); + } + } + ``` + +=== "Bytecode" + + ``` + // class version 52.0 (52) + // access flags 0x21 + public class target/exercise1/DemoClass { + + // compiled from: DemoClass.java + + // access flags 0x1 + public ()V + L0 + LINENUMBER 3 L0 + ALOAD 0 + INVOKESPECIAL java/lang/Object. ()V + RETURN + L1 + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 + MAXSTACK = 1 + MAXLOCALS = 1 + + // access flags 0x1 + public voidMethod()V + L0 + LINENUMBER 5 L0 + GETSTATIC java/lang/System.out : Ljava/io/PrintStream; + LDC "In voidMethod()." + INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V + L1 + LINENUMBER 6 L1 + RETURN + L2 + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L2 0 + MAXSTACK = 2 + MAXLOCALS = 1 + } + ``` + diff --git a/docs/jimple-values.md b/docs/jimple-values.md new file mode 100644 index 00000000000..cdcb1cc35b8 --- /dev/null +++ b/docs/jimple-values.md @@ -0,0 +1,217 @@ +# Jimple Values + +### Immediate +An `Immediate` has a [**given**]{as in constant or immutable} Type and consists of a Local ("a Variable", "Something that contains a Value") or a Constant ("Something that is a Value"). + +#### Local +``` +i0 +``` + +A Local is a variable and its scope is inside its method i.e. no referencing from outside a method. +Values can be assigned to Locals via JIdentityStmt or JAssignStmt. + +=== "Jimple" + + ```jimple + public class target.exercise1.DemoClass extends java.lang.Object + { + public void () + { + target.exercise1.DemoClass this; + this := @this: target.exercise1.DemoClass; + specialinvoke this.()>(); + return; + } + + public void compute() + { + java.io.PrintStream $stack2, $stack3; + target.exercise1.DemoClass this; + int local2; + + this := @this: target.exercise1.DemoClass; + $stack2 = ; + virtualinvoke $stack2.(1); + + local2 = this.; + $stack3 = ; + virtualinvoke $stack3.(local2); + return; + } + } + /* + $stack2, this, $stack3, local2 are all Locals. + + "this := @this: target.exercise1.DemoClass" is a JIdentityStmt assigning to a Local. + + "$stack2 = ", + "local2 = this.", + "$stack3 = " + are JAssignStmts assigning to a Local. + + */ + ``` + +=== "Java" + + ```java + package target.exercise1; + + public class DemoClass { + + private int global; + + public void compute(){ + int local; + local = 1; + System.out.println(local); + local = this.global; + System.out.println(local); + } + } + ``` + +=== "Bytecode" + + ``` + // class version 52.0 (52) + // access flags 0x21 + public class target/exercise1/DemoClass { + + // compiled from: DemoClass.java + + // access flags 0x2 + private I global + + // access flags 0x1 + public ()V + L0 + LINENUMBER 3 L0 + ALOAD 0 + INVOKESPECIAL java/lang/Object. ()V + RETURN + L1 + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 + MAXSTACK = 1 + MAXLOCALS = 1 + + // access flags 0x1 + public compute()V + L0 + LINENUMBER 9 L0 + ICONST_1 + ISTORE 1 + L1 + LINENUMBER 10 L1 + GETSTATIC java/lang/System.out : Ljava/io/PrintStream; + ILOAD 1 + INVOKEVIRTUAL java/io/PrintStream.println (I)V + L2 + LINENUMBER 11 L2 + ALOAD 0 + GETFIELD target/exercise1/DemoClass.global : I + ISTORE 1 + L3 + LINENUMBER 12 L3 + GETSTATIC java/lang/System.out : Ljava/io/PrintStream; + ILOAD 1 + INVOKEVIRTUAL java/io/PrintStream.println (I)V + L4 + LINENUMBER 14 L4 + RETURN + L5 + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L5 0 + LOCALVARIABLE local I L1 L5 1 + MAXSTACK = 2 + MAXLOCALS = 2 + } + ``` + + + +#### Constant +represents an actual value itself like `42` or `"This is a String"`. +Constants are usually assigned to `Local`s or `Ref`s. +There exists a constant entity for every [Primitive Type](jimple-types.md). + +### Expr +An expression is a language construct that calculates an operation and returns a value. +E.g. a binary operation `AbstracBinopExpr` such as an addition `a + b`, an `AbstractInvokeExpr` such as `virtualinvoke $stack2.(1);` or an `UnaryExpr` such as `!valid`. +And a bunch more! + +### Ref +#### JArrayRef +``` +$arr[1] = 42; +$anotherLocal = arr[99]; +``` +referencing an array position. + +#### JFieldRef +`JFieldRef`s are referencing a `SootField` via its FieldSignature + +- `JStaticFieldRef` like ` ` +- `JInstanceFieldRef` like ` r1.` + You can see the JInstanceFieldRef has the corresponding Local instance that points to the instance of the object which is holding the field. + + +#### IdentityRef +The IdentityRef makes those implicit special value assignments explicit. + +##### JThisRef +``` +@this: package.fruit.Banana +``` +represents the this pointer of the current class. + +##### JCaughtExceptionRef +``` +@caughtexception +``` +represents the value of the thrown exception (caught by this exceptionhandler). + +##### JParameterRef +``` +i0 := @parameter0 +i1 := @parameter1 +``` +represents a parameter of a method, identified by its index. + +## Good to know +A lot of the SootUp APIs return the `Value` Interface. To determine and handle its subtypes you can make use of instanceof. +=== "instanceOf & If-Else" + ```java + + Value op = assignStmt.getRightOp(); + if(op instanceof Local){ + // found a Local + ... + }else if(stmt instanceof Constant){ + // found a Constant + ... + }else ... + + ``` + +But this could escalate to a huge if-else-tree - almost a forest. To mitigate such scenario you can implement a subclass of `AbstractValueVisitor`. +Just subclass the methods to the respective `Value`s you need to handle. This is visitor acts like a switch-case, implemented via two dynamic calls. +=== "StmtVisitor" + ```java + + Value op = assignStmt.getRightOp() ; + AbstractValueVisitor visitor = new AbstractValueVisitor() { + private int intConstantCounter = 0; + @Override + public void caseConstant(@Nonnull Constant c) { + intConstantCounter++; + setResult(intConstantCounter); + } + }; + + op.accept(visitor); + int amountOfIfStmts = visitor.getResult(); + ``` + + If you only need a visitor for a subset of Value, you can consider ImmediateVisitor, ConstantVisitor, ExprVisitor, RefVisitor. + Sidenote: Of course its possible can create a subclass instead of an anonymous class. diff --git a/docs/jimple.md b/docs/jimple.md index 007cd9baff8..7370de698ae 100644 --- a/docs/jimple.md +++ b/docs/jimple.md @@ -6,11 +6,10 @@ Therefore, Jimple aims to bring the best of both worlds, a non-stack-based and f For this purpose Jimple was designed as a representation of JVM bytecode which is human readable. !!! info - To learn more about jimple, refer to the [thesis](https://courses.cs.washington.edu/courses/cse501/01wi/project/sable-thesis.pdf) by Raja Vallee-Rai. -It might help to visualize how the Jimple version of a Java code looks like. Have a look at the following example on the `HelloWorld` class. +Lets have a look at the following Jimple code representing Java code of a `HelloWorld` class. === "Jimple" @@ -55,7 +54,7 @@ It might help to visualize how the Jimple version of a Java code looks like. Hav } ``` -=== "Byte Code" +=== "Bytecode" ``` // class version 52.0 (52) @@ -95,6 +94,12 @@ It might help to visualize how the Jimple version of a Java code looks like. Hav } ``` +The Java Sourcecode is the easiest representation - So why all the fuzz and just use that? +Sometimes we have no access to the sourcecode but have a binary with the bytecode. +For most People reading bytecode is not that intuitive. So SootUp generates Jimple from the bytecode. +Jimple is very verbose, but makes everything explicit, that the JVM does implicitly and transforms the stack-machine strategy by a register-machine strategy i.e. Variable (`Local`) handling . + + ## Jimple Grammar Structure Jimple mimics the JVMs class file structure. Therefore it is object oriented. @@ -103,224 +108,9 @@ Three-Address-Code which means there are no nested expressions. (nested expressions can be modeled via Locals that store intermediate calculation results.) -### Class (or Interface) -A class consists of Fields and Methods. -It is referenced by its ClassType. - -=== "Jimple" - - ```jimple - public class target.exercise1.DemoClass extends java.lang.Object - { - public void () - { - target.exercise1.DemoClass this; - this := @this: target.exercise1.DemoClass; - specialinvoke this.()>(); - return; - } - } - ``` - -=== "Java" - - ```java - package target.exercise1; - - public class DemoClass {} - ``` - -=== "Byte Code" - - ``` - // class version 52.0 (52) - // access flags 0x21 - public class target/exercise1/DemoClass { - - // compiled from: DemoClass.java - - // access flags 0x1 - public ()V - L0 - LINENUMBER 3 L0 - ALOAD 0 - INVOKESPECIAL java/lang/Object. ()V - RETURN - L1 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 - MAXSTACK = 1 - MAXLOCALS = 1 - } - ``` - - -### Field -A Field is a piece of memory which can store a value that is accessible according to its visibility modifier. -It is referenced by its FieldSignature. - -=== "Jimple" - - ```jimple - public class target.exercise1.DemoClass extends java.lang.Object - { - public void () - { - target.exercise1.DemoClass this; - this := @this: target.exercise1.DemoClass; - specialinvoke this.()>(); - this. = 3.14; - return; - } - } - /* - "this." is JInstanceFieldRef - */ - ``` - -=== "Java" - - ```java - package target.exercise1; - - public class DemoClass { - private final double pi = 3.14; - } - ``` - -=== "Byte Code" - - ``` - // class version 52.0 (52) - // access flags 0x21 - public class target/exercise1/DemoClass { - - // compiled from: DemoClass.java - - // access flags 0x12 - private final D pi = 3.14 - - // access flags 0x1 - public ()V - L0 - LINENUMBER 3 L0 - ALOAD 0 - INVOKESPECIAL java/lang/Object. ()V - L1 - LINENUMBER 4 L1 - ALOAD 0 - LDC 3.14 - PUTFIELD target/exercise1/DemoClass.pi : D - RETURN - L2 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L2 0 - MAXSTACK = 3 - MAXLOCALS = 1 - } - ``` - - -### Method and the Body -The interesting part is a method. A method is a "piece of code" that can be executed. -It is referenced by its MethodSignature and contains a [**StmtGraph**]{a control flow graph} that models the sequence of single instructions/statements (Stmts). - -=== "Jimple" - - ```jimple - public class target.exercise1.DemoClass extends java.lang.Object - { - public void () - { - target.exercise1.DemoClass this; - this := @this: target.exercise1.DemoClass; - specialinvoke this.()>(); - virtualinvoke this.(); - return; - } - - public void demoMethod() - { - java.io.PrintStream $stack1; - target.exercise1.DemoClass this; - - this := @this: target.exercise1.DemoClass; - $stack1 = ; - - virtualinvoke $stack1.("Inside method."); - return; - } - } - /* - "" - and "()>" - are instances of SootMethod - */ - ``` - -=== "Java" - - ```java - package target.exercise1; - - public class DemoClass { - DemoClass(){ - demoMethod(); - } - public void demoMethod(){ - System.out.println("Inside method."); - } - } - ``` - -=== "Byte Code" - - ``` - // class version 52.0 (52) - // access flags 0x21 - public class target/exercise1/DemoClass { - - // compiled from: DemoClass.java - - // access flags 0x0 - ()V - L0 - LINENUMBER 5 L0 - ALOAD 0 - INVOKESPECIAL java/lang/Object. ()V - L1 - LINENUMBER 6 L1 - ALOAD 0 - INVOKEVIRTUAL target/exercise1/DemoClass.demoMethod ()V - L2 - LINENUMBER 7 L2 - RETURN - L3 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L3 0 - MAXSTACK = 1 - MAXLOCALS = 1 - - // access flags 0x1 - public demoMethod()V - L0 - LINENUMBER 10 L0 - GETSTATIC java/lang/System.out : Ljava/io/PrintStream; - LDC "Inside method." - INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V - L1 - LINENUMBER 11 L1 - RETURN - L2 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L2 0 - MAXSTACK = 2 - MAXLOCALS = 1 - } - ``` - - -### Signatures -Signatures are required for identifying or referencing things across a method, such as Classes, Interfaces, Methods or Fields. -Locals, on the other hand, do not need signatures, since they are referenced within method boundaries. +### Signatures and ClassTypes +Signatures are used to identify Classes,Methods or Fields uniquely/globally. +Sidenote: Locals, do not have a signature, since they are referenced within method boundaries. === "Jimple" @@ -392,7 +182,7 @@ Locals, on the other hand, do not need signatures, since they are referenced wit } ``` -=== "Byte Code" +=== "Bytecode" ``` // class version 52.0 (52) @@ -456,8 +246,9 @@ Locals, on the other hand, do not need signatures, since they are referenced wit } ``` -### Trap -A Trap is a mechanism to model exceptional flow. +### SootClass +A `SootClass` consists of SootFields and SootMethods. +It is referenced by its global identifier the `ClassType` like `java.lang.String`. === "Jimple" @@ -471,65 +262,18 @@ A Trap is a mechanism to model exceptional flow. specialinvoke this.()>(); return; } - - public void divideExample(int, int) - { - int x, y, $stack4; - java.io.PrintStream $stack5, $stack7; - java.lang.Exception $stack6; - target.exercise1.DemoClass this; - - this := @this: target.exercise1.DemoClass; - x := @parameter0: int; - y := @parameter1: int; - - label1: - $stack5 = ; - $stack4 = x / y; - virtualinvoke $stack5.($stack4); - - label2: - goto label4; - - label3: - $stack6 := @caughtexception; - $stack7 = ; - virtualinvoke $stack7.("Exception caught"); - - label4: - return; - - catch java.lang.Exception from label1 to label2 with label3; - } } - /* - By calling getTraps() method, we can get the Traip chain. - For the above jimple code, we have the below trap: - Trap : - begin : $stack5 = - end : goto [?= return] - handler: $stack6 := @caughtexception - */ ``` === "Java" ```java - package target.exercise1; + package target.exercise1; - public class DemoClass { - public void divideExample(int x, int y){ - try { - System.out.println(x / y); - }catch (Exception e){ - System.out.println("Exception caught"); - } - } - } + public class DemoClass {} ``` -=== "Byte Code" +=== "Bytecode" ``` // class version 52.0 (52) @@ -539,63 +283,23 @@ A Trap is a mechanism to model exceptional flow. // compiled from: DemoClass.java // access flags 0x1 - public ()V + public ()V L0 LINENUMBER 3 L0 ALOAD 0 INVOKESPECIAL java/lang/Object. ()V RETURN - L1 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 - MAXSTACK = 1 - MAXLOCALS = 1 - - // access flags 0x1 - public divideExample(II)V - TRYCATCHBLOCK L0 L1 L2 java/lang/Exception - L0 - LINENUMBER 6 L0 - GETSTATIC java/lang/System.out : Ljava/io/PrintStream; - ILOAD 1 - ILOAD 2 - IDIV - INVOKEVIRTUAL java/io/PrintStream.println (I)V L1 - LINENUMBER 9 L1 - GOTO L3 - L2 - LINENUMBER 7 L2 - FRAME SAME1 java/lang/Exception - ASTORE 3 - L4 - LINENUMBER 8 L4 - GETSTATIC java/lang/System.out : Ljava/io/PrintStream; - LDC "Exception caught" - INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V - L3 - LINENUMBER 10 L3 - FRAME SAME - RETURN - L5 - LOCALVARIABLE e Ljava/lang/Exception; L4 L3 3 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L5 0 - LOCALVARIABLE x I L0 L5 1 - LOCALVARIABLE y I L0 L5 2 - MAXSTACK = 3 - MAXLOCALS = 4 + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 + MAXSTACK = 1 + MAXLOCALS = 1 } ``` -### Stmt -The main piece of Jimple is a Statement (Stmt). [**Stmts**]{formerly known as Units} represent that can be executed by the JVM. - - -#### Branching Statements -A BranchingStmt's job is to model the flow between Stmts. - -##### JGotoStmt -for unconditional flow. +### SootField +A SootField is a piece of memory which can store a value that is accessible according to its visibility modifier. +It is referenced by its FieldSignature like ` `. === "Jimple" @@ -607,51 +311,26 @@ for unconditional flow. target.exercise1.DemoClass this; this := @this: target.exercise1.DemoClass; specialinvoke this.()>(); + this. = 3.14; return; } - public static void sampleMethod() - { - int i; - i = 0; - - label1: - if i >= 5 goto label3; - if i != 3 goto label2; - goto label3; - - label2: - i = i + 1; - goto label1; - - label3: - return; - } } /* - Here for statements "goto label3;" and "goto label1;", - we have two instances of JGotoStmt : - "goto[?=return]" and "goto[?=(branch)]". + "this." is JInstanceFieldRef */ ``` === "Java" ```java - package target.exercise1; + package target.exercise1; - public class DemoClass { - public static void sampleMethod(){ - label1: - for (int i = 0; i < 5; i++){ - if(i == 3){ - break label1; - } - } - } - } + public class DemoClass { + private final double pi = 3.14; + } ``` -=== "Byte Code" +=== "Bytecode" ``` // class version 52.0 (52) @@ -660,1188 +339,32 @@ for unconditional flow. // compiled from: DemoClass.java - // access flags 0x1 - public ()V - L0 - LINENUMBER 3 L0 - ALOAD 0 - INVOKESPECIAL java/lang/Object. ()V - RETURN - L1 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 - MAXSTACK = 1 - MAXLOCALS = 1 - - // access flags 0x9 - public static sampleMethod()V - L0 - LINENUMBER 6 L0 - ICONST_0 - ISTORE 0 - L1 - FRAME APPEND [I] - ILOAD 0 - ICONST_5 - IF_ICMPGE L2 - L3 - LINENUMBER 7 L3 - ILOAD 0 - ICONST_3 - IF_ICMPNE L4 - L5 - LINENUMBER 8 L5 - GOTO L2 - L4 - LINENUMBER 6 L4 - FRAME SAME - IINC 0 1 - GOTO L1 - L2 - LINENUMBER 11 L2 - FRAME CHOP 1 - RETURN - LOCALVARIABLE i I L1 L2 0 - MAXSTACK = 2 - MAXLOCALS = 1 - } - ``` - -##### JIfStmt -for conditional flow depending on boolean Expression (AbstractConditionExpr) so they have two successor Stmt's. - -=== "Jimple" - - ```jimple - public class target.exercise1.DemoClass extends java.lang.Object - { - public void () - { - target.exercise1.DemoClass this; - this := @this: target.exercise1.DemoClass; - specialinvoke this.()>(); - return; - } - - public static void sampleMethod(int) - { - int x, $stack1; - java.io.PrintStream $stack2, $stack3; - - x := @parameter0: int; - - $stack1 = x % 2; - if $stack1 != 0 goto label1; - - $stack3 = ; - virtualinvoke $stack3.("Even"); - goto label2; - - label1: - $stack2 = ; - virtualinvoke $stack2.("Odd"); - - label2: - return; - } - } - /* - For statement "if $stack1 != 0 goto label1;", - we have an instance of JIfStmt : - "if $stack1 != 0 goto $stack2 - = ". - */ - ``` - -=== "Java" - - ```java - package target.exercise1; - - public class DemoClass { - public static void sampleMethod(int x){ - if(x % 2 == 0){ - System.out.println("Even"); - }else{ - System.out.println("Odd"); - } - } - } - ``` - -=== "Byte Code" - - ``` - // class version 52.0 (52) - // access flags 0x21 - public class target/exercise1/DemoClass { - - // compiled from: DemoClass.java - - // access flags 0x1 - public ()V - L0 - LINENUMBER 3 L0 - ALOAD 0 - INVOKESPECIAL java/lang/Object. ()V - RETURN - L1 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 - MAXSTACK = 1 - MAXLOCALS = 1 - - // access flags 0x9 - public static sampleMethod(I)V - L0 - LINENUMBER 5 L0 - ILOAD 0 - ICONST_2 - IREM - IFNE L1 - L2 - LINENUMBER 6 L2 - GETSTATIC java/lang/System.out : Ljava/io/PrintStream; - LDC "Even" - INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V - GOTO L3 - L1 - LINENUMBER 8 L1 - FRAME SAME - GETSTATIC java/lang/System.out : Ljava/io/PrintStream; - LDC "Odd" - INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V - L3 - LINENUMBER 10 L3 - FRAME SAME - RETURN - L4 - LOCALVARIABLE x I L0 L4 0 - MAXSTACK = 2 - MAXLOCALS = 1 - } - ``` - -##### JSwitchStmt -for conditional flow that behaves like a switch-case. It has #numberOfCaseLabels+1 (for default) successor Stmt's. - -All other Stmts are not manipulating the flow, which means they have a single successor Stmt as long as they are not exiting the flow inside a method. - -=== "Jimple" - - ```jimple - public class target.exercise1.DemoClass extends java.lang.Object - { - public void () - { - target.exercise1.DemoClass this; - this := @this: target.exercise1.DemoClass; - specialinvoke this.()>(); - return; - } - - public void switchExample(int) - { - int x; - java.io.PrintStream $stack2, $stack3, $stack4; - target.exercise1.DemoClass this; - - this := @this: target.exercise1.DemoClass; - x := @parameter0: int; - - lookupswitch(x) - { - case 1: goto label1; - case 2: goto label2; - default: goto label3; - }; - - label1: - $stack3 = ; - virtualinvoke $stack3.("Input 1"); - goto label4; - - label2: - $stack2 = ; - virtualinvoke $stack2.("Input 2"); - goto label4; - - label3: - $stack4 = ; - virtualinvoke $stack4.("Input more than 2"); - - label4: - return; - } - } - /* - Here for below statement: - lookupswitch(x) - { - case 1: goto label1; - case 2: goto label2; - default: goto label3; - }; - - we have an instance of JLookupSwitchStmt : - lookupswitch(x) - { - case 1: goto $stack3 - = ; - case 2: goto $stack2 - = ; - default: goto $stack4 - = ; - } - */ - ``` - -=== "Java" - - ```java - package target.exercise1; - - public class DemoClass { - public void switchExample(int x){ - switch (x){ - case 1: - System.out.println("Input 1"); - break; - - case 2: - System.out.println("Input 2"); - break; - - default: - System.out.println("Input more than 2"); - break; - - } - } - } - ``` - -=== "Byte Code" - - ``` - // class version 52.0 (52) - // access flags 0x21 - public class target/exercise1/DemoClass { - - // compiled from: DemoClass.java - - // access flags 0x1 - public ()V - L0 - LINENUMBER 3 L0 - ALOAD 0 - INVOKESPECIAL java/lang/Object. ()V - RETURN - L1 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 - MAXSTACK = 1 - MAXLOCALS = 1 - - // access flags 0x1 - public switchExample(I)V - L0 - LINENUMBER 5 L0 - ILOAD 1 - LOOKUPSWITCH - 1: L1 - 2: L2 - default: L3 - L1 - LINENUMBER 7 L1 - FRAME SAME - GETSTATIC java/lang/System.out : Ljava/io/PrintStream; - LDC "Input 1" - INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V - L4 - LINENUMBER 8 L4 - GOTO L5 - L2 - LINENUMBER 11 L2 - FRAME SAME - GETSTATIC java/lang/System.out : Ljava/io/PrintStream; - LDC "Input 2" - INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V - L6 - LINENUMBER 12 L6 - GOTO L5 - L3 - LINENUMBER 15 L3 - FRAME SAME - GETSTATIC java/lang/System.out : Ljava/io/PrintStream; - LDC "Input more than 2" - INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V - L5 - LINENUMBER 19 L5 - FRAME SAME - RETURN - L7 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L7 0 - LOCALVARIABLE x I L0 L7 1 - MAXSTACK = 2 - MAXLOCALS = 2 - } - ``` - - -##### JReturnStmt & JReturnVoidStmt -They end the execution/flow inside the current method and return (a value) to its caller. - -=== "Jimple" - - ```jimple - public class target.exercise1.DemoClass extends java.lang.Object - { - public void () - { - target.exercise1.DemoClass this; - this := @this: target.exercise1.DemoClass; - specialinvoke this.()>(); - return; - } - - public int increment(int) - { - int x, $stack2; - target.exercise1.DemoClass this; - - this := @this: target.exercise1.DemoClass; - x := @parameter0: int; - - $stack2 = x + 1; - return $stack2; - } - - public void print() - { - java.io.PrintStream $stack1; - target.exercise1.DemoClass this; - - this := @this: target.exercise1.DemoClass; - $stack1 = ; - virtualinvoke $stack1.("Inside method print"); - return; - } - } - /* - "return $stack2" is JReturnStmt. - "return" is JReturnVoidStmt. - */ - ``` - -=== "Java" - - ```java - package target.exercise1; - - public class DemoClass { - public int increment(int x){ - return x + 1; - } - public void print(){ - System.out.println("Inside method print"); - } - } - ``` - -=== "Byte Code" - - ``` - // class version 52.0 (52) - // access flags 0x21 - public class target/exercise1/DemoClass { - - // compiled from: DemoClass.java - - // access flags 0x1 - public ()V - L0 - LINENUMBER 3 L0 - ALOAD 0 - INVOKESPECIAL java/lang/Object. ()V - RETURN - L1 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 - MAXSTACK = 1 - MAXLOCALS = 1 - - // access flags 0x1 - public increment(I)I - L0 - LINENUMBER 5 L0 - ILOAD 1 - ICONST_1 - IADD - IRETURN - L1 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 - LOCALVARIABLE x I L0 L1 1 - MAXSTACK = 2 - MAXLOCALS = 2 - - // access flags 0x1 - public print()V - L0 - LINENUMBER 8 L0 - GETSTATIC java/lang/System.out : Ljava/io/PrintStream; - LDC "Inside method print" - INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V - L1 - LINENUMBER 9 L1 - RETURN - L2 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L2 0 - MAXSTACK = 2 - MAXLOCALS = 1 - } - ``` - - -##### JThrowStmt -Ends the execution inside the current Method if the thrown exception is not caught by a Trap, which redirects the execution to an exceptionhandler. - - -=== "Jimple" - - ```jimple - public class target.exercise1.DemoClass extends java.lang.Object - { - public void () - { - target.exercise1.DemoClass this; - this := @this: target.exercise1.DemoClass; - specialinvoke this.()>(); - return; - } - - public void divideExample(int, int) - { - int y, x, $stack6; - java.lang.StringBuilder $stack3, $stack5, $stack7; - java.io.PrintStream $stack4; - java.lang.String $stack8; - java.lang.RuntimeException $stack9; - target.exercise1.DemoClass this; - - this := @this: target.exercise1.DemoClass; - x := @parameter0: int; - y := @parameter1: int; - - if y != 0 goto label1; - - $stack9 = new java.lang.RuntimeException; - specialinvoke $stack9.(java.lang.String)>("Divide by zero error"); - throw $stack9; - - label1: - $stack4 = ; - $stack3 = new java.lang.StringBuilder; - specialinvoke $stack3.()>(); - - $stack5 = virtualinvoke $stack3.("Divide result : "); - $stack6 = x / y; - $stack7 = virtualinvoke $stack5.($stack6); - $stack8 = virtualinvoke $stack7.(); - - virtualinvoke $stack4.($stack8); - return; - } - } - /* - "throw $stack9" is JThrowStmt. - */ - ``` - -=== "Java" - - ```java - package target.exercise1; - - public class DemoClass { - public void divideExample(int x, int y){ - if(y == 0){ - throw new RuntimeException("Divide by zero error"); - } - System.out.println("Divide result : " + x / y); - } - } - ``` - -=== "Byte Code" - - ``` - // class version 52.0 (52) - // access flags 0x21 - public class target/exercise1/DemoClass { - - // compiled from: DemoClass.java - - // access flags 0x1 - public ()V - L0 - LINENUMBER 3 L0 - ALOAD 0 - INVOKESPECIAL java/lang/Object. ()V - RETURN - L1 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 - MAXSTACK = 1 - MAXLOCALS = 1 - - // access flags 0x1 - public divideExample(II)V - L0 - LINENUMBER 5 L0 - ILOAD 2 - IFNE L1 - L2 - LINENUMBER 6 L2 - NEW java/lang/RuntimeException - DUP - LDC "Divide by zero error" - INVOKESPECIAL java/lang/RuntimeException. - (Ljava/lang/String;)V - ATHROW - L1 - LINENUMBER 8 L1 - FRAME SAME - GETSTATIC java/lang/System.out : Ljava/io/PrintStream; - NEW java/lang/StringBuilder - DUP - INVOKESPECIAL java/lang/StringBuilder. ()V - LDC "Divide result : " - INVOKEVIRTUAL java/lang/StringBuilder.append - (Ljava/lang/String;)Ljava/lang/StringBuilder; - ILOAD 1 - ILOAD 2 - IDIV - INVOKEVIRTUAL java/lang/StringBuilder.append - (I)Ljava/lang/StringBuilder; - INVOKEVIRTUAL java/lang/StringBuilder.toString - ()Ljava/lang/String; - INVOKEVIRTUAL java/io/PrintStream.println - (Ljava/lang/String;)V - L3 - LINENUMBER 9 L3 - RETURN - L4 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L4 0 - LOCALVARIABLE x I L0 L4 1 - LOCALVARIABLE y I L0 L4 2 - MAXSTACK = 4 - MAXLOCALS = 3 - } - ``` - - -##### JInvokeStmt -transfers the control flow to another method until the called method returns. - -=== "Jimple" - - ```jimple - public class target.exercise1.DemoClass extends java.lang.Object - { - public void () - { - target.exercise1.DemoClass this; - this := @this: target.exercise1.DemoClass; - specialinvoke this.()>(); - return; - } - - public void print(int) - { - target.exercise1.DemoClass this; - int x, a; - java.io.PrintStream $stack4, $stack6; - - this := @this: target.exercise1.DemoClass; - x := @parameter0: int; - - a = virtualinvoke this.(x); - $stack4 = ; - virtualinvoke $stack4.(a); - - a = virtualinvoke this.(a); - $stack6 = ; - virtualinvoke $stack6.(a); - - return; - } - - public int increment(int) - { - int x, $stack2; - target.exercise1.DemoClass this; - - this := @this: target.exercise1.DemoClass; - x := @parameter0: int; - - $stack2 = x + 1; - return $stack2; - } - } - /* - "specialinvoke this.()>()", - "virtualinvoke this.(x)", - "virtualinvoke this.(a)" - are JInvokeStmts. - */ - ``` - -=== "Java" - - ```java - package target.exercise1; - - public class DemoClass { - public void print(int x){ - int a = increment(x); - System.out.println(a); - a = increment(a); - System.out.println(a); - } - public int increment(int x){ - return x + 1; - } - } - ``` - -=== "Byte Code" - - ``` - // class version 52.0 (52) - // access flags 0x21 - public class target/exercise1/DemoClass { - - // compiled from: DemoClass.java - - // access flags 0x1 - public ()V - L0 - LINENUMBER 3 L0 - ALOAD 0 - INVOKESPECIAL java/lang/Object. ()V - RETURN - L1 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 - MAXSTACK = 1 - MAXLOCALS = 1 - - // access flags 0x1 - public print(I)V - L0 - LINENUMBER 5 L0 - ALOAD 0 - ILOAD 1 - INVOKEVIRTUAL target/exercise1/DemoClass.increment (I)I - ISTORE 2 - L1 - LINENUMBER 6 L1 - GETSTATIC java/lang/System.out : Ljava/io/PrintStream; - ILOAD 2 - INVOKEVIRTUAL java/io/PrintStream.println (I)V - L2 - LINENUMBER 7 L2 - ALOAD 0 - ILOAD 2 - INVOKEVIRTUAL target/exercise1/DemoClass.increment (I)I - ISTORE 2 - L3 - LINENUMBER 8 L3 - GETSTATIC java/lang/System.out : Ljava/io/PrintStream; - ILOAD 2 - INVOKEVIRTUAL java/io/PrintStream.println (I)V - L4 - LINENUMBER 9 L4 - RETURN - L5 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L5 0 - LOCALVARIABLE x I L0 L5 1 - LOCALVARIABLE a I L1 L5 2 - MAXSTACK = 2 - MAXLOCALS = 3 - - // access flags 0x1 - public increment(I)I - L0 - LINENUMBER 11 L0 - ILOAD 1 - ICONST_1 - IADD - IRETURN - L1 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 - LOCALVARIABLE x I L0 L1 1 - MAXSTACK = 2 - MAXLOCALS = 2 - } - ``` - - -##### JAssignStmt -assigns a Value from the right hand-side to the left hand-side. -Left hand-side of an assignment can be a Local referencing a variable (i.e. a Local) or a FieldRef referencing a Field. -Right hand-side of an assignment can be an expression (Expr), a Local, a FieldRef or a Constant. - -=== "Jimple" - - ```jimple - public class target.exercise1.DemoClass extends java.lang.Object - { - public void () - { - target.exercise1.DemoClass this; - this := @this: target.exercise1.DemoClass; - specialinvoke this.()>(); - this. = 0; - return; - } - - public int updateCounter() - { - target.exercise1.DemoClass this; - int $stack1, $stack2, $stack3; - - this := @this: target.exercise1.DemoClass; - - $stack1 = this.; - $stack2 = $stack1 + 1; - this. = $stack2; - $stack3 = this.; - - return $stack3; - } - } - /* - "this. = 0", - "$stack1 = this.", - "$stack2 = $stack1 + 1" - "this. = $stack2" - "$stack3 = this." - are JAssignStmts. - */ - ``` - -=== "Java" - - ```java - package target.exercise1; - - public class DemoClass { - private int counter = 0; - public int updateCounter(){ - counter = counter + 1; - return counter; - } - } - ``` - -=== "Byte Code" - - ``` - // class version 52.0 (52) - // access flags 0x21 - public class target/exercise1/DemoClass { - - // compiled from: DemoClass.java - - // access flags 0x2 - private I counter - - // access flags 0x1 - public ()V - L0 - LINENUMBER 3 L0 - ALOAD 0 - INVOKESPECIAL java/lang/Object. ()V - L1 - LINENUMBER 4 L1 - ALOAD 0 - ICONST_0 - PUTFIELD target/exercise1/DemoClass.counter : I - RETURN - L2 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L2 0 - MAXSTACK = 2 - MAXLOCALS = 1 - - // access flags 0x1 - public updateCounter()I - L0 - LINENUMBER 6 L0 - ALOAD 0 - ALOAD 0 - GETFIELD target/exercise1/DemoClass.counter : I - ICONST_1 - IADD - PUTFIELD target/exercise1/DemoClass.counter : I - L1 - LINENUMBER 7 L1 - ALOAD 0 - GETFIELD target/exercise1/DemoClass.counter : I - IRETURN - L2 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L2 0 - MAXSTACK = 3 - MAXLOCALS = 1 - } - ``` - - -##### JIdentityStmt -is semantically like the JAssignStmt and handles assignments of IdentityRef's to make implicit assignments explicit into the StmtGraph. - -=== "Jimple" - - ```jimple - public class target.exercise1.DemoClass extends java.lang.Object - { - public void () - { - target.exercise1.DemoClass this; - this := @this: target.exercise1.DemoClass; - specialinvoke this.()>(); - return; - } - - public void DemoClass(int) - { - target.exercise1.DemoClass this; - int counter; - - this := @this: target.exercise1.DemoClass; - counter := @parameter0: int; - this. = counter; - return; - } - } - /* - "this := @this: target.exercise1.DemoClass" and - "counter := @parameter0: int" are JIdentityStmts - */ - ``` - -=== "Java" - - ```java - package target.exercise1; - - public class DemoClass { - private int counter; - public void DemoClass(int counter){ - this.counter = counter; - } - } - ``` - -=== "Byte Code" - - ``` - // class version 52.0 (52) - // access flags 0x21 - public class target/exercise1/DemoClass { - - // compiled from: DemoClass.java - - // access flags 0x2 - private I counter - - // access flags 0x1 - public ()V - L0 - LINENUMBER 3 L0 - ALOAD 0 - INVOKESPECIAL java/lang/Object. ()V - RETURN - L1 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 - MAXSTACK = 1 - MAXLOCALS = 1 - - // access flags 0x1 - public DemoClass(I)V - L0 - LINENUMBER 6 L0 - ALOAD 0 - ILOAD 1 - PUTFIELD target/exercise1/DemoClass.counter : I - L1 - LINENUMBER 7 L1 - RETURN - L2 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L2 0 - LOCALVARIABLE counter I L0 L2 1 - MAXSTACK = 2 - MAXLOCALS = 2 - } - ``` - - -#####JEnterMonitorStmt & JExitMonitorStmt -marks synchronized blocks of code from JEnterMonitorStmt to JExitMonitorStmt. - -=== "Jimple" - - ```jimple - public class target.exercise1.DemoClass extends java.lang.Object - { - public void () - { - target.exercise1.DemoClass this; - this := @this: target.exercise1.DemoClass; - specialinvoke this.()>(); - this. = 0; - return; - } - - public int updateCounter() - { - target.exercise1.DemoClass this; - int $stack4, $stack5, $stack7; - java.lang.Throwable $stack8; - - this := @this: target.exercise1.DemoClass; - - entermonitor this; - - label1: - $stack4 = this.; - $stack5 = $stack4 + 1; - this. = $stack5; - - exitmonitor this; - - label2: - goto label5; - - label3: - $stack8 := @caughtexception; - - exitmonitor this; - - label4: - throw $stack8; - - label5: - $stack7 = this.; - return $stack7; - - catch java.lang.Throwable from label1 to label2 with label3; - catch java.lang.Throwable from label3 to label4 with label3; - } - } - /* - "entermonitor this" is JEnterMonitorStmt. - "exitmonitor this" is JExitMonitorStmt. - */ - ``` - -=== "Java" - - ```java - package target.exercise1; - - public class DemoClass { - private int counter = 0; - public int updateCounter(){ - synchronized (this) { - counter = counter + 1; - } - return counter; - } - } - ``` - -=== "Byte Code" - - ``` - // class version 52.0 (52) - // access flags 0x21 - public class target/exercise1/DemoClass { - - // compiled from: DemoClass.java - - // access flags 0x2 - private I counter - - // access flags 0x1 - public ()V - L0 - LINENUMBER 3 L0 - ALOAD 0 - INVOKESPECIAL java/lang/Object. ()V - L1 - LINENUMBER 4 L1 - ALOAD 0 - ICONST_0 - PUTFIELD target/exercise1/DemoClass.counter : I - RETURN - L2 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L2 0 - MAXSTACK = 2 - MAXLOCALS = 1 - - // access flags 0x1 - public updateCounter()I - TRYCATCHBLOCK L0 L1 L2 null - TRYCATCHBLOCK L2 L3 L2 null - L4 - LINENUMBER 6 L4 - ALOAD 0 - DUP - ASTORE 1 - MONITORENTER - L0 - LINENUMBER 7 L0 - ALOAD 0 - ALOAD 0 - GETFIELD target/exercise1/DemoClass.counter : I - ICONST_1 - IADD - PUTFIELD target/exercise1/DemoClass.counter : I - L5 - LINENUMBER 8 L5 - ALOAD 1 - MONITOREXIT - L1 - GOTO L6 - L2 - FRAME FULL [target/exercise1/DemoClass java/lang/Object] - [java/lang/Throwable] - ASTORE 2 - ALOAD 1 - MONITOREXIT - L3 - ALOAD 2 - ATHROW - L6 - LINENUMBER 9 L6 - FRAME CHOP 1 - ALOAD 0 - GETFIELD target/exercise1/DemoClass.counter : I - IRETURN - L7 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L4 L7 0 - MAXSTACK = 3 - MAXLOCALS = 3 - } - ``` - - -##### JRetStmt -##### JBreakpointStmt -models a Breakpoint set by a Debugger (usually not relevant for static analyses) - - -### Immediate -An Immediate has a [**given**]{as in constant or immutable} Type and consists of a Local ("a Variable", "Something that contains a Value") or a Constant ("Something that is a Value"). - - -### Type -VoidType - -=== "Jimple" - - ```jimple - public class target.exercise1.DemoClass extends java.lang.Object - { - public void () - { - target.exercise1.DemoClass this; - this := @this: target.exercise1.DemoClass; - specialinvoke this.()>(); - return; - } - - public void voidMethod() - { - java.io.PrintStream $stack1; - target.exercise1.DemoClass this; - this := @this: target.exercise1.DemoClass; - $stack1 = ; - virtualinvoke $stack1.("In voidMethod()."); - return; - } - } - /* - For the SootMethod - , - returnType is instance of VoidType. - */ - ``` - -=== "Java" - - ```java - package target.exercise1; - - public class DemoClass { - public void voidMethod(){ - System.out.println("In voidMethod()."); - } - } - ``` - -=== "Byte Code" - - ``` - // class version 52.0 (52) - // access flags 0x21 - public class target/exercise1/DemoClass { + // access flags 0x12 + private final D pi = 3.14 - // compiled from: DemoClass.java - // access flags 0x1 public ()V L0 - LINENUMBER 3 L0 + LINENUMBER 3 L0 ALOAD 0 INVOKESPECIAL java/lang/Object. ()V - RETURN - L1 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 - MAXSTACK = 1 - MAXLOCALS = 1 - - // access flags 0x1 - public voidMethod()V - L0 - LINENUMBER 5 L0 - GETSTATIC java/lang/System.out : Ljava/io/PrintStream; - LDC "In voidMethod()." - INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V L1 - LINENUMBER 6 L1 + LINENUMBER 4 L1 + ALOAD 0 + LDC 3.14 + PUTFIELD target/exercise1/DemoClass.pi : D RETURN L2 LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L2 0 - MAXSTACK = 2 + MAXSTACK = 3 MAXLOCALS = 1 } ``` -#### PrimaryType -BooleanType, ByteType, CharType, ShortType, IntType, LongType, DoubleType, FloatType +### SootMethod and its Body +The interesting part is a method. A method is a "piece of code" that can be executed. +It is referenced by its MethodSignature like ` `. === "Jimple" @@ -1850,314 +373,31 @@ BooleanType, ByteType, CharType, ShortType, IntType, LongType, DoubleType, Float { public void () { - target.exercise1.DemoClass this; - this := @this: target.exercise1.DemoClass; - specialinvoke this.()>(); - return; - } - - - public void display() - { - java.io.PrintStream $stack11, $stack13, $stack15, - $stack17, $stack19, $stack21, $stack23, $stack25; - int $stack12, $stack14, $stack16, $stack18; - long $stack20; - double $stack22; - float $stack24; - target.exercise1.DemoClass this; - boolean $stack26; - - this := @this: target.exercise1.DemoClass; - - $stack11 = ; - - goto label1; - - label1: - $stack26 = 0; - virtualinvoke $stack11.($stack26); - - $stack13 = ; - $stack12 = 127 - 1; - virtualinvoke $stack13.($stack12); - - $stack15 = ; - $stack14 = 97 + 1; - virtualinvoke $stack15.($stack14); - - $stack17 = ; - $stack16 = 1123 + 1; - virtualinvoke $stack17.($stack16); - - $stack19 = ; - $stack18 = 123456 + 1; - virtualinvoke $stack19.($stack18); - - $stack21 = ; - $stack20 = 10L + 1L; - virtualinvoke $stack21.($stack20); - - $stack23 = ; - $stack22 = 10.1 + 1.0; - virtualinvoke $stack23.($stack22); - - $stack25 = ; - $stack24 = 10.1F + 1.0F; - virtualinvoke $stack25.($stack24); - - return; - } - } - /* - The JimpleLocal $stack12, $stack14, $stack16, $stack18 are of IntType. - Similarly, $stack20 is of LongType, $stack22 is of DoubleType and so on. - */ - ``` - -=== "Java" - - ```java - package target.exercise1; - - public class DemoClass { - public void display(){ - boolean varBoolean = true; - byte varByte = 127; - char varChar = 'a'; - short varShort = 1123; - int varInt = 123456; - long varLong = 10L; - double varDouble = 10.10; - float varFloat = 10.10f; - - System.out.println(!varBoolean); - System.out.println(varByte-1); - System.out.println(varChar+1); - System.out.println(varShort+1); - System.out.println(varInt+1); - System.out.println(varLong+1); - System.out.println(varDouble+1); - System.out.println(varFloat+1); - + target.exercise1.DemoClass this; + this := @this: target.exercise1.DemoClass; + specialinvoke this.()>(); + virtualinvoke this.(); + return; } - } - ``` - -=== "Byte Code" - - ``` - // class version 52.0 (52) - // access flags 0x21 - public class target/exercise1/DemoClass { - - // compiled from: DemoClass.java - - // access flags 0x1 - public ()V - L0 - LINENUMBER 3 L0 - ALOAD 0 - INVOKESPECIAL java/lang/Object. ()V - RETURN - L1 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 - MAXSTACK = 1 - MAXLOCALS = 1 - - // access flags 0x1 - public display()V - L0 - LINENUMBER 5 L0 - ICONST_1 - ISTORE 1 - L1 - LINENUMBER 6 L1 - BIPUSH 127 - ISTORE 2 - L2 - LINENUMBER 7 L2 - BIPUSH 97 - ISTORE 3 - L3 - LINENUMBER 8 L3 - SIPUSH 1123 - ISTORE 4 - L4 - LINENUMBER 9 L4 - LDC 123456 - ISTORE 5 - L5 - LINENUMBER 10 L5 - LDC 10 - LSTORE 6 - L6 - LINENUMBER 11 L6 - LDC 10.1 - DSTORE 8 - L7 - LINENUMBER 12 L7 - LDC 10.1 - FSTORE 10 - L8 - LINENUMBER 14 L8 - GETSTATIC java/lang/System.out : Ljava/io/PrintStream; - ILOAD 1 - IFNE L9 - ICONST_1 - GOTO L10 - L9 - FRAME FULL [target/exercise1/DemoClass I I I I I J D F] - [java/io/PrintStream] - ICONST_0 - L10 - FRAME FULL [target/exercise1/DemoClass I I I I I J D F] - [java/io/PrintStream I] - INVOKEVIRTUAL java/io/PrintStream.println (Z)V - L11 - LINENUMBER 15 L11 - GETSTATIC java/lang/System.out : Ljava/io/PrintStream; - ILOAD 2 - ICONST_1 - ISUB - INVOKEVIRTUAL java/io/PrintStream.println (I)V - L12 - LINENUMBER 16 L12 - GETSTATIC java/lang/System.out : Ljava/io/PrintStream; - ILOAD 3 - ICONST_1 - IADD - INVOKEVIRTUAL java/io/PrintStream.println (I)V - L13 - LINENUMBER 17 L13 - GETSTATIC java/lang/System.out : Ljava/io/PrintStream; - ILOAD 4 - ICONST_1 - IADD - INVOKEVIRTUAL java/io/PrintStream.println (I)V - L14 - LINENUMBER 18 L14 - GETSTATIC java/lang/System.out : Ljava/io/PrintStream; - ILOAD 5 - ICONST_1 - IADD - INVOKEVIRTUAL java/io/PrintStream.println (I)V - L15 - LINENUMBER 19 L15 - GETSTATIC java/lang/System.out : Ljava/io/PrintStream; - LLOAD 6 - LCONST_1 - LADD - INVOKEVIRTUAL java/io/PrintStream.println (J)V - L16 - LINENUMBER 20 L16 - GETSTATIC java/lang/System.out : Ljava/io/PrintStream; - DLOAD 8 - DCONST_1 - DADD - INVOKEVIRTUAL java/io/PrintStream.println (D)V - L17 - LINENUMBER 21 L17 - GETSTATIC java/lang/System.out : Ljava/io/PrintStream; - FLOAD 10 - FCONST_1 - FADD - INVOKEVIRTUAL java/io/PrintStream.println (F)V - L18 - LINENUMBER 23 L18 - RETURN - L19 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L19 0 - LOCALVARIABLE varBoolean Z L1 L19 1 - LOCALVARIABLE varByte B L2 L19 2 - LOCALVARIABLE varChar C L3 L19 3 - LOCALVARIABLE varShort S L4 L19 4 - LOCALVARIABLE varInt I L5 L19 5 - LOCALVARIABLE varLong J L6 L19 6 - LOCALVARIABLE varDouble D L7 L19 8 - LOCALVARIABLE varFloat F L8 L19 10 - MAXSTACK = 5 - MAXLOCALS = 11 - } - ``` - - -#### ReferenceType -ClassType, -ArrayType -NullType - -=== "Jimple" - ```jimple - public class target.exercise1.DemoClass extends java.lang.Object - { - public void () + public void demoMethod() { - target.exercise1.DemoClass this; - this := @this: target.exercise1.DemoClass; - specialinvoke this.()>(); - return; - } + java.io.PrintStream $stack1; + target.exercise1.DemoClass this; - public target.exercise1.DemoClass getObject(target.exercise1.DemoClass) - { - target.exercise1.DemoClass obj, this; - this := @this: target.exercise1.DemoClass; - obj := @parameter0: target.exercise1.DemoClass; - return obj; - } + this := @this: target.exercise1.DemoClass; + $stack1 = ; - public void compute(boolean) - { - int[] b; - java.io.PrintStream $stack5, $stack6; - boolean check; - target.exercise1.DemoClass this; - int i; - null_type r0; - java.lang.NullPointerException soot0; - this := @this: target.exercise1.DemoClass; - check := @parameter0: boolean; - b = newarray (int)[5]; - i = 0; - - label1: - if i >= 5 goto label3; - if check == 0 goto label2; - r0 = (null_type) i; - soot0 = new java.lang.NullPointerException; - specialinvoke soot0.(java.lang.String)> - ("This statement would have triggered an Exception: a[i#1] = r0"); - throw soot0; - - label2: - b[i] = i; - i = i + 1; - goto label1; - - label3: - $stack5 = ; - virtualinvoke $stack5.(b); - $stack6 = ; - virtualinvoke $stack6.(null); - return; + virtualinvoke $stack1.("Inside method."); + return; } } /* - The JimpleLocal b is of ArrayType, - and JimpleLocal r0 is of NullType. + "" + and "()>" + are instances of SootMethod */ ``` @@ -2167,296 +407,57 @@ NullType package target.exercise1; public class DemoClass { - - public DemoClass getObject(DemoClass obj){ - return obj; - } - - public void compute(boolean check){ - int a[] = null; - int b[] = new int[5]; - for (int i = 0; i < 5; i++) { - if(check){ - a[i] = i; - } - b[i] = i; - } - System.out.println(b); - System.out.println(a); + DemoClass(){ + demoMethod(); + } + public void demoMethod(){ + System.out.println("Inside method."); } } ``` -=== "Byte Code" +=== "Bytecode" ``` - // class version 52.0 (52) + // class version 52.0 (52) // access flags 0x21 public class target/exercise1/DemoClass { // compiled from: DemoClass.java - // access flags 0x1 - public ()V + // access flags 0x0 + ()V L0 - LINENUMBER 3 L0 + LINENUMBER 5 L0 ALOAD 0 INVOKESPECIAL java/lang/Object. ()V - RETURN L1 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 + LINENUMBER 6 L1 + ALOAD 0 + INVOKEVIRTUAL target/exercise1/DemoClass.demoMethod ()V + L2 + LINENUMBER 7 L2 + RETURN + L3 + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L3 0 MAXSTACK = 1 MAXLOCALS = 1 // access flags 0x1 - public getObject(Ltarget/exercise1/DemoClass;)Ltarget/exercise1/DemoClass; - L0 - LINENUMBER 6 L0 - ALOAD 1 - ARETURN - L1 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 - LOCALVARIABLE obj Ltarget/exercise1/DemoClass; L0 L1 1 - MAXSTACK = 1 - MAXLOCALS = 2 - - // access flags 0x1 - public compute(Z)V + public demoMethod()V L0 LINENUMBER 10 L0 - ACONST_NULL - ASTORE 2 + GETSTATIC java/lang/System.out : Ljava/io/PrintStream; + LDC "Inside method." + INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V L1 LINENUMBER 11 L1 - ICONST_5 - NEWARRAY T_INT - ASTORE 3 - L2 - LINENUMBER 12 L2 - ICONST_0 - ISTORE 4 - L3 - FRAME APPEND [[I [I I] - ILOAD 4 - ICONST_5 - IF_ICMPGE L4 - L5 - LINENUMBER 13 L5 - ILOAD 1 - IFEQ L6 - L7 - LINENUMBER 14 L7 - ALOAD 2 - ILOAD 4 - ILOAD 4 - IASTORE - L6 - LINENUMBER 16 L6 - FRAME SAME - ALOAD 3 - ILOAD 4 - ILOAD 4 - IASTORE - L8 - LINENUMBER 12 L8 - IINC 4 1 - GOTO L3 - L4 - LINENUMBER 18 L4 - FRAME CHOP 1 - GETSTATIC java/lang/System.out : Ljava/io/PrintStream; - ALOAD 3 - INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V - L9 - LINENUMBER 19 L9 - GETSTATIC java/lang/System.out : Ljava/io/PrintStream; - ALOAD 2 - INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V - L10 - LINENUMBER 20 L10 RETURN - L11 - LOCALVARIABLE i I L3 L4 4 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L11 0 - LOCALVARIABLE check Z L0 L11 1 - LOCALVARIABLE a [I L1 L11 2 - LOCALVARIABLE b [I L2 L11 3 - MAXSTACK = 3 - MAXLOCALS = 5 - } - ``` - - -#### Local -```jimple -i0 -``` -A Local is a variable and its scope is inside its method i.e. no referencing from outside a method. -Values can be assigned to Locals via JIdentityStmt or JAssignStmt. - -=== "Jimple" - - ```jimple - public class target.exercise1.DemoClass extends java.lang.Object - { - public void () - { - target.exercise1.DemoClass this; - this := @this: target.exercise1.DemoClass; - specialinvoke this.()>(); - return; - } - - public void compute() - { - java.io.PrintStream $stack2, $stack3; - target.exercise1.DemoClass this; - int local#2; - - this := @this: target.exercise1.DemoClass; - $stack2 = ; - virtualinvoke $stack2.(1); - - local#2 = this.; - $stack3 = ; - virtualinvoke $stack3.(local#2); - return; - } - } - /* - $stack2, this, $stack3, local#2 are all JimpleLocal. - - "this := @this: target.exercise1.DemoClass" is JIdentityStmt - - "$stack2 = ", - "local#2 = this.", - "$stack3 = " - are JAssignStmt - - */ - ``` - -=== "Java" - - ```java - package target.exercise1; - - public class DemoClass { - - private int global; - - public void compute(){ - int local; - local = 1; - System.out.println(local); - local = this.global; - System.out.println(local); - } - } - ``` - -=== "Byte Code" - - ``` - // class version 52.0 (52) - // access flags 0x21 - public class target/exercise1/DemoClass { - - // compiled from: DemoClass.java - - // access flags 0x2 - private I global - - // access flags 0x1 - public ()V - L0 - LINENUMBER 3 L0 - ALOAD 0 - INVOKESPECIAL java/lang/Object. ()V - RETURN - L1 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L1 0 - MAXSTACK = 1 - MAXLOCALS = 1 - - // access flags 0x1 - public compute()V - L0 - LINENUMBER 9 L0 - ICONST_1 - ISTORE 1 - L1 - LINENUMBER 10 L1 - GETSTATIC java/lang/System.out : Ljava/io/PrintStream; - ILOAD 1 - INVOKEVIRTUAL java/io/PrintStream.println (I)V L2 - LINENUMBER 11 L2 - ALOAD 0 - GETFIELD target/exercise1/DemoClass.global : I - ISTORE 1 - L3 - LINENUMBER 12 L3 - GETSTATIC java/lang/System.out : Ljava/io/PrintStream; - ILOAD 1 - INVOKEVIRTUAL java/io/PrintStream.println (I)V - L4 - LINENUMBER 14 L4 - RETURN - L5 - LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L5 0 - LOCALVARIABLE local I L1 L5 1 - MAXSTACK = 2 - MAXLOCALS = 2 - } + LOCALVARIABLE this Ltarget/exercise1/DemoClass; L0 L2 0 + MAXSTACK = 2 + MAXLOCALS = 1 + } ``` - - -#### Constant -represents a value itself. don't confuse it with a variable/Local which has a immutable (i.e. final) attribute. - -There exists a constant entity for every Type - that way all value types can have a representation. - - -### Expr -An expression is a language construct that returns a value. E.g. a binary operation such as addition. - - -### Ref -#### JArrayRef -```jimple -$arr[1] -``` -referencing a position inside an array. - -#### JFieldRef (JStaticFieldRef & JInstanceFieldRef) -```jimple - -// or -r1. -``` -referencing a Field via its FieldSignature and if necessary (i.e. with JInstanceFieldRef) the corresponding Local instance that points to the object instance. - -#### IdentityRef -The IdentityRef makes those implicit special value assignments explicit. - -##### JThisRef -```jimple -@this: package.fruit.Banana -``` -represents the this pointer of the current class. - -##### JCaughtExceptionRef -```jimple -@caughtexception -``` -represents the value of the thrown exception (caught by this exceptionhandler). - -##### JParameterRef -```jimple -i0 := @parameter0 -i1 := @parameter1 -``` -represents a parameter of a method, identified by its index. - +More about the [Body](jimple-body.md) of the SootMethod. \ No newline at end of file diff --git a/docs/migrating.md b/docs/migrating.md new file mode 100644 index 00000000000..dc2eca2059c --- /dev/null +++ b/docs/migrating.md @@ -0,0 +1,116 @@ +# Migration Help + +### Version 1.3.0 +- The Typehierarchy API is now returning `Stream` instead of `Collection`. The simplest fix to have the same behaviour as before would be to collect the Stream on your own ( e.g. via `.collect(Collectors.toList())` ). +- Default BytecodeBodyinterceptors are enabled to improve Jimple. To mitigate that adapt the List of BodyInterceptors to your needs. + +### Version 1.2.0 +- The (Java)Project structure was removed. You can configure the (Java)View directly. +- Bodyinterceptors are now passed as arguments into AnalysisInputLocations. + +### From [Soot](https://github.com/soot-oss/Soot) +- The Scene singleton is dead. Long live the Scene. We have a central View object(!) now. +- Library first! No command line tool as primary goal. So you can configure parts of SootUp (near) where it is actually used. +- t.b.c. + +Below we show a comparison of the code so far with the same functionality in sootup. + +=== "SootUp" + + ``` java + AnalysisInputLocation inputLocation = + new JavaClassPathAnalysisInputLocation("path2Binary"); + + JavaView view = new JavaView(inputLocation); + + JavaClassType classType = + view.getIdentifierFactory().getClassType("HelloWorld"); + + MethodSignature methodSignature = + view + .getIdentifierFactory() + .getMethodSignature( + "main", classType, "void", + Collections.singletonList("java.lang.String[]")); + + JavaSootClass sootClass = view.getClass(classType).get(); + + MethodSubSignature mss = methodSignature.getSubSignature(); + JavaSootMethod sootMethod = sootClass.getMethod(mss).get(); + + sootMethod.getBody().getStmts(); + ``` + +=== "Soot" + + ``` java + G.reset(); + String userdir = System.getProperty("user.dir"); + String sootCp = + userdir + + File.separator + + "target" + + File.separator + + "test-classes" + + File.pathSeparator + "lib"+File.separator+"rt.jar"; + + Options.v().set_soot_classpath(sootCp); + Options.v().set_whole_program(true); + Options.v().setPhaseOption("cg.cha", "on"); + Options.v().setPhaseOption("cg", "all-reachable:true"); + Options.v().set_no_bodies_for_excluded(true); + Options.v().set_allow_phantom_refs(true); + Options.v().setPhaseOption("jb", "use-original-names:true"); + Options.v().set_prepend_classpath(false); + + Scene.v().addBasicClass("java.lang.StringBuilder"); + SootClass c = + Scene.v().forceResolve(targetTestClassName, SootClass.BODIES); + if (c != null) { + c.setApplicationClass(); + } + Scene.v().loadNecessaryClasses(); + + SootMethod method; + for (SootClass c : Scene.v().getApplicationClasses()) { + if(c.getName().equals("example.HelloWorld")){ + for (SootMethod m : c.getMethods()) { + if (!m.hasActiveBody()) { + continue; + } + if (m.getName().equals("entryPoint")) { + method = m; + break; + } + } + } + } + + method.getActiveBody().getUnits(); + ``` + + + + + + + + + diff --git a/docs/overrides/partials/footer.html b/docs/overrides/partials/footer.html new file mode 100644 index 00000000000..12330e0318a --- /dev/null +++ b/docs/overrides/partials/footer.html @@ -0,0 +1,108 @@ + + + + \ No newline at end of file diff --git a/docs/overrides/partials/outdated.html b/docs/overrides/partials/outdated.html new file mode 100644 index 00000000000..36a4967d7de --- /dev/null +++ b/docs/overrides/partials/outdated.html @@ -0,0 +1,8 @@ +{% extends "base.html" %} + +{% block outdated %} +You're not viewing the latest version. + + Click here to go to latest version. + +{% endblock %} \ No newline at end of file diff --git a/docs/qilin.md b/docs/qilin.md new file mode 100644 index 00000000000..cc424b81e4f --- /dev/null +++ b/docs/qilin.md @@ -0,0 +1,89 @@ +# Incorporate Qilin Pointer Analysis + +### Dependencies + +=== "Maven" + ```maven + + org.soot-oss + sootup.java.sourcecode + {{ git_latest_release }} + + ``` + +=== "Gradle" + + ```groovy + compile "org.soot-oss:sootup.qilin:{{ git_latest_release }}" + ``` + +### How to create a pointer analysis + +!!! note "WIP: Beware most likely the API will change so you only need to specify SootUp objects!" + +One can create an Andersen's context-insensitive analysis with following code: + +=== "Java" + + ```Java + String entrypoint = "dacapo.antlr.Main"; + PTAPattern ptaPattern = new PTAPattern("insens"); + PTA pta = PTAFactory.createPTA(ptaPattern, view, entrypoint); + pta.run(); + ``` + +Users must specify the program's `View`, select a `PTAPattern` +(indicating the desired types of pointer analyses to perform), +and designate the `entrypoint` - which is serving as the entry point for the analysis. + +### How to use pointer analysis results + +First, we can use Qilin's pointer analysis to get a On-the-Fly constructed callgraph: + +```java +OnFlyCallGraph cg = pta.getCallGraph(); +``` + +Second, we can use it to get the points-to results for some interested local variables, fields, etc. + +```java +PointsToSet pts0 = pta.reachingObjects(method, v0); +PointsToSet pts1 = pta.reachingObjects(method, v1, f); // PTS(v1.f) +``` + +Third, we can check whether two variables, `a` and `b`, are aliases by checking +whether there is an object that exists in both of their points-to sets. + +Qilin does not currently offer a `isMayAlias` API within the PTA class. +However, a similar functionality can be found in `qilin.test.util.AliasAssertion` with the method: +```boolean isMayAlias(PTA pta, Value va, Value vb)``` +This method allows to check for potential aliasing between two values given a PTA instance. + +### A Full list of Pointer Analyses + +[Qilin](https://github.com/QilinPTA/Qilin)'s toolbox includes a rich set of pointer analyses, which are given below: + +Note that the symbol **k** used in the table should be replaced with a concrete small constant like 1 or 2. + +| PTA patterns | Description | Reference | +|----------------------------|---------------------------------------------------------------|---------------------------------------------------------------------------| +| **insens** | Andersen's context-insensitive analysis | [Paper](https://link.springer.com/chapter/10.1007/3-540-36579-6_12) | +| **k**c | **k**-callsite-sensitive pointer analysis (denoted **k**CFA). | [Paper](https://www.cse.psu.edu/~trj1/cse598-f11/docs/sharir_pnueli1.pdf) | +| **k**o | **k**-object-sensitive pointer analysis (denoted **k**OBJ). | [Paper](https://dl.acm.org/doi/abs/10.1145/1044834.1044835) | +| **k**t | **k**-type-sensitive pointer analysis (denoted **k**TYPE). | [Paper](https://dl.acm.org/doi/abs/10.1145/1926385.1926390) | +| **k**h | hybrid **k**-object-sensitive pointer analysis. | [Paper](https://dl.acm.org/doi/10.1145/2499370.2462191) | +| **k**ht | hybrid **k**-type-sensitive pointer analysis. | [Paper](https://dl.acm.org/doi/10.1145/2499370.2462191) | +| B-2o | BEAN-guided 2OBJ. Only k=2 is supported. | [Paper](https://link.springer.com/chapter/10.1007/978-3-662-53413-7_24) | +| D-2o | Data-driven 2OBJ. Only k=2 is supported. | [Paper](https://dl.acm.org/doi/10.1145/3133924) | +| D-2c | Data-driven 2CFA. Only k=2 is supported. | [Paper](https://dl.acm.org/doi/10.1145/3133924) | +| M-**k**o | MAHJONG-guided **k**OBJ. | [Paper](https://dl.acm.org/doi/10.1145/3062341.3062360) | +| M-**k**c | MAHJONG-guided **k**CFA. | [Paper](https://dl.acm.org/doi/10.1145/3062341.3062360) | +| E-**k**o | EAGLE-guided **k**OBJ. | [Paper](https://dl.acm.org/doi/10.1145/3360574) | +| T-**k**o | TURNER-guided **k**OBJ. | [Paper](https://drops.dagstuhl.de/opus/volltexte/2021/14059/) | +| Z-**k**o | ZIPPER-guided **k**OBJ. | [Paper](https://dl.acm.org/doi/10.1145/3276511) | +| Z-**k**c | ZIPPER-guided **k**CFA. | [Paper](https://dl.acm.org/doi/10.1145/3276511) | +| Z-**k**o -cd | The context debloated version of ZIPPER-guided **k**OBJ. | [Paper](https://doi.org/10.1109/ASE51524.2021.9678880) | +| **k**o -cd -cda=CONCH | The context debloated version of **k**OBJ using Conch. | [Paper](https://doi.org/10.1109/ASE51524.2021.9678880) | +| **k**o -cd -cda=DEBLOATERX | The context debloated version of **k**OBJ using DebloaterX. | [Paper](https://dl.acm.org/doi/10.1145/3622832) | +| s-**k**c | SELECTX-guided **k**CFA. | [Paper](https://doi.org/10.1007/978-3-030-88806-0_13) | + diff --git a/docs/tool_setup.md b/docs/tool_setup.md new file mode 100644 index 00000000000..93c6d00dda8 --- /dev/null +++ b/docs/tool_setup.md @@ -0,0 +1,36 @@ +# From Prototype to an intuitive Tool +**How was the parameter order again?** +For a lot of cli tools we see an arbitrary order of cli parameters, different options for giving a working directory etc.. +So in the wild you can see a lot from run.sh/run.bat to make files just to reorder arguments to execute a tool. + +In SootUp we thought we could help on improving this madness while saving your time. + +The command line parser mimics the options the java executable accepts - at least for what is supported by SootUp. +This makes it very simple to just copy the execution paramaters you use for execution, to use them more or less as is four the analysis tool. + +### Dependencies +=== "Maven" + ```maven + + commons-cli + commons-cli + 1.8.0 + + ``` + +=== "Gradle" + ```groovy + implementation("commons-cli:commons-cli:1.8.0") + ``` + +### Java Code + + ```java + + class SootUpConfiguration{ + // TODO incorporate from downstream + } + + ``` + +We are happy if you steal the following code to create a tool where the setup is just simple. diff --git a/docs/typehierarchy.md b/docs/typehierarchy.md new file mode 100644 index 00000000000..7c07b3c983d --- /dev/null +++ b/docs/typehierarchy.md @@ -0,0 +1,91 @@ +# TypeHierarchy +The TypeHierarchy models the relationship of Classes or Interfaces of a OOP program. + +## Creating TypeHierarchy + +=== "SootUp" + + ```java + String cpString = "src/test/resources/Callgraph/binary"; + List inputLocations = new ArrayList(); + inputLocations.add(new JavaClassPathAnalysisInputLocation(cpStr)); + inputLocations.add(new DefaultRTJarAnalysisInputLocation()); + + JavaView view = new JavaView(inputLocations); + TypeHierarchy typehierarchy = view.getTypeHierarchy(); + ``` + +=== "Soot" + + ```java + String userdir = System.getProperty("user.dir"); + String sootCp = userdir + File.separator + "target" + File.separator + "test-classes"+ File.pathSeparator + "lib"+File.separator+"rt.jar"; + + String targetTestClassName = target.exercise1.Hierarchy.class.getName(); + G.reset(); + Options.v().set_whole_program(true); + Options.v().set_soot_classpath(sootCp); + Options.v().set_no_bodies_for_excluded(true); + Options.v().process_dir(); + Options.v().set_allow_phantom_refs(true); + Options.v().setPhaseOption("jb", "use-original-names:true"); + Options.v().set_prepend_classpath(false); + SootClass c = Scene.v().forceResolve(targetTestClassName, SootClass.BODIES); + if (c != null) + c.setApplicationClass(); + Scene.v().loadNecessaryClasses(); + + Hierarchy hierarchy = new Hierarchy(); + + ``` + +## Create a JavaClassType + +=== "SootUp" + + ```java + JavaClassType classTypeA = view.getIdentifierFactory().getClassType("packageName.A"); + JavaClassType classTypeB = view.getIdentifierFactory().getClassType("packageName.B"); + JavaClassType classTypeC = view.getIdentifierFactory().getClassType("packageName.C"); + + ``` +=== "Soot" + + ```java + String targetTestClassName = "packageNameA.A"; + SootClass methodA = Scene.v().getSootClass(targetTestClassName); + + ``` + +## Query the TypeHierarchy +### Classes +```java + // if the assertion fails, the following methods will throw an Exception (you don't have to call it - it's just to illustrate the assumption) + assert typehierarchy.contains(classTypeA); + + typehierarchy.superclassOf(classTypeA); + + typehierarchy.subclassesOf(classTypeA); + + typehierarchy.isSubtypeOf(classTypeA, classTypeB); + +``` + +### Interfaces + +```java + JavaClassType iterableInterface = view.getIdentifierFactory().getClassType("java.lang.Iterable"); + + // if any of the assertions fail, the following methods will throw an Exception (you don't have to call these - it's just to illustrate the assumptions) + assert typehierarchy.contains(iterableInterface); + assert typehierarchy.isInterface(iterableInterface); + + // transitive relations as well + typehierarchy.implementedInterfacesOf(iterableInterface); + typehierarchy.implementersOf(iterableInterface); + + // only the direct related relations + typehierarchy.directlyImplementedInterfacesOf(iterableInterface); + typehierarchy.directlyExtendedInterfacesOf(iterableInterface); + +``` \ No newline at end of file diff --git a/docs/write_analyses.md b/docs/write_analyses.md index 7ee09b1b10e..5604ea1e47c 100644 --- a/docs/write_analyses.md +++ b/docs/write_analyses.md @@ -1,7 +1,34 @@ -# Writing Analyses +# Write your own Interprocedural Dataflow Analysis + +### Dependencies +=== "Maven" + ```maven + + org.soot-oss + sootup.analysis + + + ``` +=== "Gradle" + ```groovy + compile "org.soot-oss:sootup.analysis:{{ git_latest_release }}" + ``` + +### Useful Knowledge +Background Knowledge as online lectures are available on YouTube. + +- [IFDS Framework](https://www.youtube.com/watch?v=QkK79fT0TFU&ab_channel=SecureSoftwareEngineering) +- [IDE Framework](https://www.youtube.com/watch?v=0uMHX3UY9bg&ab_channel=SecureSoftwareEngineering) + +### Examples +Taint Analysis, TypeState Analysis, Linear Constant Propagation, ... ``` -// TODO improve tutorial! +// TODO incorporate & guide through examples + +In the meantime please have a look into the test cases of the +analysis submodule to see example implementations of interprocedural +data-flow analysis via the IFDS or IDE Framework. + ``` -If you wish to implement an interprocedural data-flow analysis via the IFDS or IDE Framework please have a look at the test cases in the sootup.analysis submodule. \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index baf4d12f4bf..c20dbc5db63 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,54 +1,66 @@ -site_name: SootUp +site_name: "" site_url: https://soot-oss.github.io/SootUp/ repo_url: https://github.com/soot-oss/SootUp/ edit_uri: edit/develop/docs/ nav: - - Home: index.md - - Announcements: announce.md - - Design Decisions: whatsnew.md - - - Basics: + - Getting Started: - Installation: installation.md -# - Configure your Input: analysisinputlocations.md - - Getting started: getting-started.md - - Jimple: jimple.md + - First Steps: getting-started.md + - Analysis Input: analysisinput.md + - Examples: examples.md + + - Basics: + - Jimple IR: jimple.md + - Jimple Body: jimple-body.md + - Jimple Statements: jimple-stmts.md + - Jimple Types: jimple-types.md + - Jimple Values: jimple-values.md - - Advanced Topics: + - Advanced Topics: - BodyInterceptors: bodyinterceptors.md - - Call Graph Construction: call-graph-construction.md - - SootUp Utilities: advanced-topics.md + - TypeHierarchy: typehierarchy.md + - Callgraphs: callgraphs.md + - BuiltIn Analyses: builtin-analyses.md - - How to..: - - Write an Analysis: write_analyses.md - # - Modify a StmtGraph: mutable_stmtgraph.md - # - Modify a View: mutable_view.md - # - Implement a BodyInterceptor: body_interceptor.md - # - Implement an AnalysisTool: write_analysis_tool.md + - How to..: + - Write a Dataflow analysis: write_analyses.md + - Incorporate Pointer Analysis: qilin.md + # - Modify a StmtGraph: mutable_stmtgraph.md + # - Modify a View: mutable_view.md + # - Implement a BodyInterceptor: body_interceptor.md + # - Implement an AnalysisTool: write_analysis_tool.md + # - From Prototype to Tool: tool_setup.md - - More information: - - Javadoc: /SootUp/apidocs + - Misc & More information: + - Announcements: announcement.md + - Design Decisions: whatsnew.md + - Migration Help: migrating.md + - Latest Javadoc: /SootUp/apidocs - Troubleshooting & FAQ: faq.md - # - Based on SootUp: tools.md + # - Based on SootUp: tools.md + theme: - palette: - primary: white name: material - logo: ./SootUpLogo.svg - favicon: ./icon.svg + logo: ./img/SootUpLogo.svg + favicon: ./img/icon.svg + palette: + primary: custom + custom_dir: ./docs/overrides features: - navigation.sections +extra_css: + - css/customizetheme.css + - css/hint.min.css + plugins: - tooltips - search - include: src_path: 'sootup.examples/src/test/java/sootup/examples' -extra_css: - - css/hint.min.css - markdown_extensions: - pymdownx.highlight: linenums: true diff --git a/pom.xml b/pom.xml index c4f76d7c6fe..388c73d77e9 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ pom org.soot-oss sootup - 1.2.1-SNAPSHOT + 1.3.1-SNAPSHOT SootUp A new version of Soot with a completely overhauled architecture https://soot-oss.github.io/SootUp @@ -67,6 +67,7 @@ sootup.java.sourcecode sootup.tests sootup.callgraph + sootup.qilin sootup.analysis sootup.examples sootup.report diff --git a/shared-test-resources/bugfixes/Indy.java b/shared-test-resources/bugfixes/Indy.java index 98441621cd4..cb6ff5ce2a3 100644 --- a/shared-test-resources/bugfixes/Indy.java +++ b/shared-test-resources/bugfixes/Indy.java @@ -1,6 +1,3 @@ -import java.time.LocalDate; -import java.time.Period; -import java.util.stream.Stream; import java.util.stream.IntStream; /** conversion failed when there is a merge (here: after the if) and an invokedynamic followed */ diff --git a/sootup.analysis/pom.xml b/sootup.analysis/pom.xml index 4c76fb3defa..7776aa8179c 100644 --- a/sootup.analysis/pom.xml +++ b/sootup.analysis/pom.xml @@ -9,7 +9,7 @@ org.soot-oss sootup - 1.2.1-SNAPSHOT + 1.3.1-SNAPSHOT diff --git a/sootup.callgraph/pom.xml b/sootup.callgraph/pom.xml index 3ded1243005..a408d80c47d 100644 --- a/sootup.callgraph/pom.xml +++ b/sootup.callgraph/pom.xml @@ -9,7 +9,7 @@ org.soot-oss sootup - 1.2.1-SNAPSHOT + 1.3.1-SNAPSHOT diff --git a/sootup.callgraph/src/main/java/sootup/callgraph/RapidTypeAnalysisAlgorithm.java b/sootup.callgraph/src/main/java/sootup/callgraph/RapidTypeAnalysisAlgorithm.java index 5dce36986ec..d8343dbf0d2 100644 --- a/sootup.callgraph/src/main/java/sootup/callgraph/RapidTypeAnalysisAlgorithm.java +++ b/sootup.callgraph/src/main/java/sootup/callgraph/RapidTypeAnalysisAlgorithm.java @@ -48,8 +48,8 @@ */ public class RapidTypeAnalysisAlgorithm extends AbstractCallGraphAlgorithm { - @Nonnull private Set instantiatedClasses = Collections.emptySet(); - @Nonnull private Map> ignoredCalls = Collections.emptyMap(); + @Nonnull protected Set instantiatedClasses = Collections.emptySet(); + @Nonnull protected Map> ignoredCalls = Collections.emptyMap(); /** * The constructor of the RTA algorithm. @@ -70,9 +70,13 @@ public CallGraph initialize() { @Nonnull @Override public CallGraph initialize(@Nonnull List entryPoints) { + // init helper data structures instantiatedClasses = new HashSet<>(); ignoredCalls = new HashMap<>(); + CallGraph cg = constructCompleteCallGraph(view, entryPoints); + + // delete the data structures instantiatedClasses = Collections.emptySet(); ignoredCalls = Collections.emptyMap(); return cg; @@ -231,27 +235,38 @@ protected void preProcessingMethod( List newInstantiatedClasses = collectInstantiatedClassesInMethod(method); newInstantiatedClasses.forEach( - instantiatedClassType -> { - List newEdges = ignoredCalls.get(instantiatedClassType); - if (newEdges != null) { - newEdges.forEach( - call -> { - MethodSignature concreteTarget = - resolveConcreteDispatch(view, call.getTargetMethodSignature()).orElse(null); - if (concreteTarget == null) { - return; - } - addCallToCG( - call.getSourceMethodSignature(), - concreteTarget, - call.getInvokableStmt(), - cg, - workList); - }); - // can be removed because the instantiated class will be considered in future resolves - ignoredCalls.remove(instantiatedClassType); - } - }); + classType -> includeIgnoredCallsToClass(classType, cg, workList)); + } + + /** + * This method will add all saved ignored calls from a given class to the call graph. All new + * targets will be added to the worklist + * + * @param classType the class type which is the target of all ignored calls + * @param cg the call graph that will be extended by the ignored calls + * @param workList the work list that will be extended by the new targets of ignored calls. + */ + protected void includeIgnoredCallsToClass( + ClassType classType, MutableCallGraph cg, Deque workList) { + List newEdges = ignoredCalls.get(classType); + if (newEdges != null) { + newEdges.forEach( + call -> { + MethodSignature concreteTarget = + resolveConcreteDispatch(view, call.getTargetMethodSignature()).orElse(null); + if (concreteTarget == null) { + return; + } + addCallToCG( + call.getSourceMethodSignature(), + concreteTarget, + call.getInvokableStmt(), + cg, + workList); + }); + // can be removed because the instantiated class will be considered in future resolves + ignoredCalls.remove(classType); + } } /** diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/source/ConcreteCall.java b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/source/ConcreteCall.java new file mode 100644 index 00000000000..403b997c6c5 --- /dev/null +++ b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/source/ConcreteCall.java @@ -0,0 +1,12 @@ +// cvc/Class.java +package cvc; + +class Class { + + public void target(){ } + + public static void main(String[] args){ + Class cls = new Class(); + cls.target(); + } +} diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/source/ConcreteCallDefaultInterface.java b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/source/ConcreteCallDefaultInterface.java new file mode 100644 index 00000000000..9db1421d553 --- /dev/null +++ b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/source/ConcreteCallDefaultInterface.java @@ -0,0 +1,16 @@ +// cvci/Class.java +package cvci; + +class Class implements Interface{ + + public static void main(String[] args){ + Class cls = new Class(); + cls.target(); + } +} + +interface Interface { + + default void target(){ } + +} diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/source/ConcreteCallDefaultSubInterface.java b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/source/ConcreteCallDefaultSubInterface.java new file mode 100644 index 00000000000..afaca67bf79 --- /dev/null +++ b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/source/ConcreteCallDefaultSubInterface.java @@ -0,0 +1,22 @@ +// cvci/Class.java +package cvcsi; + +class Class implements SubInterface{ + + public static void main(String[] args){ + Class cls = new Class(); + cls.target(); + } +} + +interface Interface { + + default void target(){ } + +} + +interface SubInterface extends Interface { + + default void target(){ } + +} diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/source/ConcreteCallSuperClassDefaultSubInterface.java b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/source/ConcreteCallSuperClassDefaultSubInterface.java new file mode 100644 index 00000000000..0e32385085a --- /dev/null +++ b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/source/ConcreteCallSuperClassDefaultSubInterface.java @@ -0,0 +1,22 @@ +// cvc/Class.java +package cvcscsi; + +class Class extends SuperClass implements Interface{ + + public static void main(String[] args){ + Class cls = new Class(); + cls.target(); + } +} + +class SuperClass implements SubInterface { + +} + +interface Interface { + default void target(){} +} + +interface SubInterface extends Interface { + default void target(){} +} \ No newline at end of file diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/source/ConcreteCallSuperClassWithDefaultInterface.java b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/source/ConcreteCallSuperClassWithDefaultInterface.java new file mode 100644 index 00000000000..f16a278fdb8 --- /dev/null +++ b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/source/ConcreteCallSuperClassWithDefaultInterface.java @@ -0,0 +1,22 @@ +// cvc/Class.java +package cvcscwi; + +class Class extends SuperClass implements Interface{ + + public static void main(String[] args){ + Class cls = new Class(); + cls.target(); + } +} + +class SuperClass { + + public void target(){ } + +} + +class Interface { + + default void target(){ } + +} \ No newline at end of file diff --git a/sootup.core/pom.xml b/sootup.core/pom.xml index fed9cc549d9..1fa9104f3d2 100644 --- a/sootup.core/pom.xml +++ b/sootup.core/pom.xml @@ -2,20 +2,22 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 + SootUp Core - - - org.jgrapht - jgrapht-core - 1.3.1 - compile - - sootup.core jar org.soot-oss sootup - 1.2.1-SNAPSHOT + 1.3.1-SNAPSHOT + + + + org.jgrapht + jgrapht-core + 1.3.1 + compile + + diff --git a/sootup.core/src/main/java/sootup/core/graph/MutableStmtGraph.java b/sootup.core/src/main/java/sootup/core/graph/MutableStmtGraph.java index a4cd6029b98..48ebfe01a54 100644 --- a/sootup.core/src/main/java/sootup/core/graph/MutableStmtGraph.java +++ b/sootup.core/src/main/java/sootup/core/graph/MutableStmtGraph.java @@ -111,7 +111,7 @@ public void setEdges(@Nonnull BranchingStmt from, @Nonnull Stmt... targets) { * removes the current outgoing flows of "from" to "to" * * @return returns List of the successor indices of "from" that were connected to "to" - items are - * 0 in case of FallsThroughStmts or idx > 0 in case of BranchingStmts with multiple + * 0 in case of FallsThroughStmts or idx > 0 in case of BranchingStmts with multiple * successors */ public abstract List removeEdge(@Nonnull Stmt from, @Nonnull Stmt to); diff --git a/sootup.core/src/main/java/sootup/core/jimple/common/expr/AbstractFloatBinopExpr.java b/sootup.core/src/main/java/sootup/core/jimple/common/expr/AbstractFloatBinopExpr.java index 5c834a32808..ce8987fe707 100644 --- a/sootup.core/src/main/java/sootup/core/jimple/common/expr/AbstractFloatBinopExpr.java +++ b/sootup.core/src/main/java/sootup/core/jimple/common/expr/AbstractFloatBinopExpr.java @@ -44,14 +44,19 @@ public Type getType() { if (Type.isIntLikeType(op1t) && Type.isIntLikeType(op2t)) { return PrimitiveType.getInt(); - } else if (op1t.equals(PrimitiveType.getLong()) || op2t.equals(PrimitiveType.getLong())) { - return PrimitiveType.getLong(); - } else if (op1t.equals(PrimitiveType.getDouble()) || op2t.equals(PrimitiveType.getDouble())) { - return PrimitiveType.getDouble(); - } else if (op1t.equals(PrimitiveType.getFloat()) || op2t.equals(PrimitiveType.getFloat())) { - return PrimitiveType.getFloat(); - } else { - return UnknownType.getInstance(); } + final PrimitiveType.LongType longType = PrimitiveType.getLong(); + if (op1t == longType || op2t == longType) { + return longType; + } + final PrimitiveType.DoubleType doubleType = PrimitiveType.getDouble(); + if (op1t == doubleType || op2t == doubleType) { + return doubleType; + } + final PrimitiveType.FloatType floatType = PrimitiveType.getFloat(); + if (op1t == floatType || op2t == floatType) { + return floatType; + } + return UnknownType.getInstance(); } } diff --git a/sootup.core/src/main/java/sootup/core/jimple/common/expr/JNewMultiArrayExpr.java b/sootup.core/src/main/java/sootup/core/jimple/common/expr/JNewMultiArrayExpr.java index f9e9d60e675..3e834e28f75 100644 --- a/sootup.core/src/main/java/sootup/core/jimple/common/expr/JNewMultiArrayExpr.java +++ b/sootup.core/src/main/java/sootup/core/jimple/common/expr/JNewMultiArrayExpr.java @@ -104,7 +104,7 @@ public ArrayType getBaseType() { return baseType; } - public Value getSize(int index) { + public Immediate getSize(@Nonnull int index) { return sizes.get(index); } diff --git a/sootup.core/src/main/java/sootup/core/jimple/common/expr/JUshrExpr.java b/sootup.core/src/main/java/sootup/core/jimple/common/expr/JUshrExpr.java index f7b9e7cf1e7..e6cc0fd1637 100644 --- a/sootup.core/src/main/java/sootup/core/jimple/common/expr/JUshrExpr.java +++ b/sootup.core/src/main/java/sootup/core/jimple/common/expr/JUshrExpr.java @@ -54,16 +54,15 @@ public Type getType() { Value op2 = getOp2(); if (Type.isIntLikeType(op2.getType())) { - return UnknownType.getInstance(); - } - if (Type.isIntLikeType(op1.getType())) { - return PrimitiveType.getInt(); - } - if (op1.getType().equals(PrimitiveType.getLong())) { - return PrimitiveType.getLong(); + if (Type.isIntLikeType(op1.getType())) { + return PrimitiveType.getInt(); + } + final PrimitiveType.LongType longType = PrimitiveType.getLong(); + if (op1.getType() == longType) { + return longType; + } } - return UnknownType.getInstance(); } diff --git a/sootup.core/src/main/java/sootup/core/jimple/javabytecode/stmt/JRetStmt.java b/sootup.core/src/main/java/sootup/core/jimple/javabytecode/stmt/JRetStmt.java index d2e1fcc36a7..3a70f99745c 100644 --- a/sootup.core/src/main/java/sootup/core/jimple/javabytecode/stmt/JRetStmt.java +++ b/sootup.core/src/main/java/sootup/core/jimple/javabytecode/stmt/JRetStmt.java @@ -34,8 +34,8 @@ import sootup.core.util.printer.StmtPrinter; /** - * Represents the deprecated JVM ret statement - which is used in JSR Context - which - * is deprecated as well + * Represents the deprecated JVM ret statement (< java 1.6) - which is used in JSR + * Context - which is deprecated as well. */ public final class JRetStmt extends AbstractStmt implements FallsThroughStmt { diff --git a/sootup.core/src/main/java/sootup/core/jimple/visitor/ReplaceUseStmtVisitor.java b/sootup.core/src/main/java/sootup/core/jimple/visitor/ReplaceUseStmtVisitor.java index 0a14e29a0f4..c68e5921aef 100644 --- a/sootup.core/src/main/java/sootup/core/jimple/visitor/ReplaceUseStmtVisitor.java +++ b/sootup.core/src/main/java/sootup/core/jimple/visitor/ReplaceUseStmtVisitor.java @@ -104,7 +104,7 @@ public void caseAssignStmt(@Nonnull JAssignStmt stmt) { } } - setResult(stmt); + defaultCaseStmt(stmt); } @Override @@ -114,12 +114,20 @@ public void caseIdentityStmt(@Nonnull JIdentityStmt stmt) { @Override public void caseEnterMonitorStmt(@Nonnull JEnterMonitorStmt stmt) { - setResult(stmt.withOp((Immediate) newUse)); + if (stmt.getOp() == oldUse) { + setResult(stmt.withOp((Immediate) newUse)); + } else { + defaultCaseStmt(stmt); + } } @Override public void caseExitMonitorStmt(@Nonnull JExitMonitorStmt stmt) { - setResult(stmt.withOp((Immediate) newUse)); + if (stmt.getOp() == oldUse) { + setResult(stmt.withOp((Immediate) newUse)); + } else { + defaultCaseStmt(stmt); + } } @Override @@ -135,7 +143,7 @@ public void caseIfStmt(@Nonnull JIfStmt stmt) { if (exprVisitor.getResult() != conditionExpr) { setResult(stmt.withCondition((AbstractConditionExpr) exprVisitor.getResult())); } else { - setResult(stmt); + defaultCaseStmt(stmt); } } @@ -146,12 +154,20 @@ public void caseNopStmt(@Nonnull JNopStmt stmt) { @Override public void caseRetStmt(@Nonnull JRetStmt stmt) { - setResult(stmt.withStmtAddress(newUse)); + if (stmt.getStmtAddress() == oldUse) { + setResult(stmt.withStmtAddress(newUse)); + } else { + defaultCaseStmt(stmt); + } } @Override public void caseReturnStmt(@Nonnull JReturnStmt stmt) { - setResult(stmt.withReturnValue((Immediate) newUse)); + if (stmt.getOp() == oldUse) { + setResult(stmt.withReturnValue((Immediate) newUse)); + } else { + defaultCaseStmt(stmt); + } } @Override @@ -161,12 +177,20 @@ public void caseReturnVoidStmt(@Nonnull JReturnVoidStmt stmt) { @Override public void caseSwitchStmt(@Nonnull JSwitchStmt stmt) { - setResult(stmt.withKey((Immediate) newUse)); + if (stmt.getKey() == oldUse) { + setResult(stmt.withKey((Immediate) newUse)); + } else { + defaultCaseStmt(stmt); + } } @Override public void caseThrowStmt(@Nonnull JThrowStmt stmt) { - setResult(stmt.withOp((Immediate) newUse)); + if (stmt.getOp() == oldUse) { + setResult(stmt.withOp((Immediate) newUse)); + } else { + defaultCaseStmt(stmt); + } } public void defaultCaseStmt(@Nonnull Stmt stmt) { diff --git a/sootup.core/src/main/java/sootup/core/model/Position.java b/sootup.core/src/main/java/sootup/core/model/Position.java index 671873f2f8e..e13877082cb 100644 --- a/sootup.core/src/main/java/sootup/core/model/Position.java +++ b/sootup.core/src/main/java/sootup/core/model/Position.java @@ -56,7 +56,7 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } - FullPosition position = (FullPosition) o; + Position position = (Position) o; return getFirstLine() == position.getFirstLine() && getFirstCol() == position.getFirstCol() && getLastLine() == position.getLastLine() diff --git a/sootup.core/src/main/java/sootup/core/signatures/MethodSubSignature.java b/sootup.core/src/main/java/sootup/core/signatures/MethodSubSignature.java index 3f8648222f0..51e641d033b 100644 --- a/sootup.core/src/main/java/sootup/core/signatures/MethodSubSignature.java +++ b/sootup.core/src/main/java/sootup/core/signatures/MethodSubSignature.java @@ -84,11 +84,6 @@ public boolean equals(Object o) { } MethodSubSignature that = (MethodSubSignature) o; - - if (!super.equals(that)) { - return false; - } - return Objects.equal(getParameterTypes(), that.getParameterTypes()); } diff --git a/sootup.core/src/main/java/sootup/core/typehierarchy/TypeHierarchy.java b/sootup.core/src/main/java/sootup/core/typehierarchy/TypeHierarchy.java index d3fdd18d6e5..4dfa5a53a4b 100644 --- a/sootup.core/src/main/java/sootup/core/typehierarchy/TypeHierarchy.java +++ b/sootup.core/src/main/java/sootup/core/typehierarchy/TypeHierarchy.java @@ -75,6 +75,7 @@ public interface TypeHierarchy { * For an interface type, this does the same as {@link #implementersOf(ClassType)}. For a class * type, this does the same as {@link #subclassesOf(ClassType)}. */ + // TODO: [ms] check! not sure this method makes sense in the interface.. @Nonnull Stream subtypesOf(@Nonnull ClassType type); diff --git a/sootup.core/src/main/java/sootup/core/typehierarchy/ViewTypeHierarchy.java b/sootup.core/src/main/java/sootup/core/typehierarchy/ViewTypeHierarchy.java index 192110019b5..6f7392529be 100644 --- a/sootup.core/src/main/java/sootup/core/typehierarchy/ViewTypeHierarchy.java +++ b/sootup.core/src/main/java/sootup/core/typehierarchy/ViewTypeHierarchy.java @@ -183,7 +183,7 @@ public Stream directlyExtendedInterfacesOf(@Nonnull ClassType interfa throw new IllegalArgumentException("Could not find " + interfaceType + " in hierarchy."); } if (vertex.type != VertexType.Interface) { - throw new IllegalArgumentException(interfaceType + " is not a class."); + throw new IllegalArgumentException(interfaceType + " is not an interface."); } return directExtendedInterfacesOf(vertex).map(v -> v.javaClassType); } diff --git a/sootup.core/src/main/java/sootup/core/types/Type.java b/sootup.core/src/main/java/sootup/core/types/Type.java index be513ee83f7..805da414e0d 100644 --- a/sootup.core/src/main/java/sootup/core/types/Type.java +++ b/sootup.core/src/main/java/sootup/core/types/Type.java @@ -36,11 +36,13 @@ public static boolean isObjectLikeType(Type type) { } public static boolean isIntLikeType(Type type) { - return type == PrimitiveType.IntType.getInstance() - || type == PrimitiveType.ByteType.getInstance() - || type == PrimitiveType.ShortType.getInstance() - || type == PrimitiveType.CharType.getInstance() - || type == PrimitiveType.BooleanType.getInstance(); + return type instanceof PrimitiveType.IntType; + /* type == PrimitiveType.IntType.getInstance() + || type == PrimitiveType.ByteType.getInstance() + || type == PrimitiveType.ShortType.getInstance() + || type == PrimitiveType.CharType.getInstance() + || type == PrimitiveType.BooleanType.getInstance(); + */ } public static boolean isObject(Type type) { @@ -68,7 +70,7 @@ public static int getValueBitSize(Type type) { if (type instanceof PrimitiveType.ByteType) { return 8; } - if (type instanceof PrimitiveType.ShortType) { + if (type instanceof PrimitiveType.ShortType || type instanceof PrimitiveType.CharType) { return 16; } if (type instanceof PrimitiveType.IntType || type instanceof PrimitiveType.FloatType) { diff --git a/sootup.core/src/test/java/sootup/core/graph/MutableBlockStmtGraphTest.java b/sootup.core/src/test/java/sootup/core/graph/MutableBlockStmtGraphTest.java index 36e20fcd926..a8e0b959a9c 100644 --- a/sootup.core/src/test/java/sootup/core/graph/MutableBlockStmtGraphTest.java +++ b/sootup.core/src/test/java/sootup/core/graph/MutableBlockStmtGraphTest.java @@ -800,8 +800,8 @@ public PackageName getPackageName() { @Test public void copyOfImmutable() { /* - Stmt stmt1 = new JNopStmt(StmtPositionInfo.createNoStmtPositionInfo()); - Stmt stmt2 = new JNopStmt(StmtPositionInfo.createNoStmtPositionInfo()); + Stmt stmt1 = new JNopStmt(StmtPositionInfo.getNoStmtPositionInfo()); + Stmt stmt2 = new JNopStmt(StmtPositionInfo.getNoStmtPositionInfo()); MutableStmtGraph graph = new MutableBlockStmtGraph(); graph.putEdge(stmt1, stmt2); graph.setStartingStmt(stmt1); diff --git a/sootup.core/src/test/java/sootup/core/types/TypeTest.java b/sootup.core/src/test/java/sootup/core/types/TypeTest.java new file mode 100644 index 00000000000..72047e83a46 --- /dev/null +++ b/sootup.core/src/test/java/sootup/core/types/TypeTest.java @@ -0,0 +1,16 @@ +package sootup.core.types; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +@Tag("Java8") +public class TypeTest { + + @Test + public void testValueBitSize() { + int charValueBitSize = Type.getValueBitSize(PrimitiveType.getChar()); + assertEquals(16, charValueBitSize); + } +} diff --git a/sootup.examples/pom.xml b/sootup.examples/pom.xml index e636c68a7af..769679c247b 100644 --- a/sootup.examples/pom.xml +++ b/sootup.examples/pom.xml @@ -8,7 +8,7 @@ org.soot-oss sootup - 1.2.1-SNAPSHOT + 1.3.1-SNAPSHOT diff --git a/sootup.java.bytecode/pom.xml b/sootup.java.bytecode/pom.xml index b5cac113ac4..a868d167877 100644 --- a/sootup.java.bytecode/pom.xml +++ b/sootup.java.bytecode/pom.xml @@ -9,7 +9,7 @@ org.soot-oss sootup - 1.2.1-SNAPSHOT + 1.3.1-SNAPSHOT diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/OperandMerging.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/OperandMerging.java index 4882ddc17b0..412d627ce01 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/OperandMerging.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/OperandMerging.java @@ -81,6 +81,7 @@ void mergeOutput(@Nonnull Operand outputOperand) { + ")."); } } + outputOperand.changeStackLocal(output.stackLocal); } } diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/inputlocation/DefaultRTJarAnalysisInputLocation.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/inputlocation/DefaultRTJarAnalysisInputLocation.java index 98850dbfbfb..c913764fc52 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/inputlocation/DefaultRTJarAnalysisInputLocation.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/inputlocation/DefaultRTJarAnalysisInputLocation.java @@ -31,6 +31,9 @@ * Refers to the rt.jar from <=Java8 as an AnalysisInputLocation requires: JAVA_HOME to be set * and expects the jar in the "lib/" subdirectory. If you need to include the rt.jar from a custom * Location please make use of JavaClassPathAnalysisInputLocation. + * + *

Info: This only works if you are running java 8 or older. Otherwise use {@link + * JrtFileSystemAnalysisInputLocation}. */ public class DefaultRTJarAnalysisInputLocation extends ArchiveBasedAnalysisInputLocation { diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/inputlocation/OTFCompileAnalysisInputLocation.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/inputlocation/OTFCompileAnalysisInputLocation.java new file mode 100644 index 00000000000..c629fff6f11 --- /dev/null +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/inputlocation/OTFCompileAnalysisInputLocation.java @@ -0,0 +1,213 @@ +package sootup.java.bytecode.inputlocation; + +/*- + * #%L + * Soot + * %% + * Copyright (C) 2018-2024 Markus Schmidt and others + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import static java.lang.System.currentTimeMillis; + +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.FileTime; +import java.util.*; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import javax.tools.*; +import sootup.core.frontend.SootClassSource; +import sootup.core.inputlocation.AnalysisInputLocation; +import sootup.core.model.SourceType; +import sootup.core.transform.BodyInterceptor; +import sootup.core.types.ClassType; +import sootup.core.views.View; +import sootup.java.core.interceptors.BytecodeBodyInterceptors; + +/** e.g. to simplify creating testcases - no manual compilation step is required */ +public class OTFCompileAnalysisInputLocation implements AnalysisInputLocation { + + private final AnalysisInputLocation inputLocation; + + /** for Java file contents as a String i.e. not as a File on the filesystem */ + public OTFCompileAnalysisInputLocation(String fileName, String compilationUnitsContent) { + this( + fileName, + compilationUnitsContent, + SourceType.Application, + BytecodeBodyInterceptors.Default.getBodyInterceptors()); + } + + public OTFCompileAnalysisInputLocation( + String fileName, + String compilationUnitsContent, + @Nonnull SourceType srcType, + @Nonnull List bodyInterceptors) { + Path compile = compile(fileName, compilationUnitsContent); + inputLocation = PathBasedAnalysisInputLocation.create(compile, srcType, bodyInterceptors); + } + + /** existing .java files */ + public OTFCompileAnalysisInputLocation(Path dotJavaFile) { + this(Collections.singletonList(dotJavaFile)); + } + + public OTFCompileAnalysisInputLocation(List dotJavaFile) { + this( + dotJavaFile, + SourceType.Application, + BytecodeBodyInterceptors.Default.getBodyInterceptors()); + } + + public OTFCompileAnalysisInputLocation( + @Nonnull List dotJavaFiles, + @Nonnull SourceType srcType, + @Nonnull List bodyInterceptors) { + Path compile = compile(dotJavaFiles); + inputLocation = PathBasedAnalysisInputLocation.create(compile, srcType, bodyInterceptors); + } + + @Nonnull + @Override + public Optional getClassSource( + @Nonnull ClassType type, @Nonnull View view) { + return inputLocation.getClassSource(type, view); + } + + @Nonnull + @Override + public Collection getClassSources(@Nonnull View view) { + return inputLocation.getClassSources(view); + } + + @Nonnull + @Override + public SourceType getSourceType() { + return inputLocation.getSourceType(); + } + + @Nonnull + @Override + public List getBodyInterceptors() { + return inputLocation.getBodyInterceptors(); + } + + private static Path getTempDirectory(String fileName) throws IOException { + return Files.createTempDirectory("sootup-otfcompile-" + fileName.hashCode()); + } + + static Path compile(String fileName, String fileContent) { + try { + Path tmp = getTempDirectory(fileName); + Path path = tmp.resolve(fileName.hashCode() + "/"); + boolean dirWasCreated = path.toFile().mkdirs(); + Path srcFile = tmp.resolve(fileName); + + if (dirWasCreated) { + Files.write(srcFile, fileContent.getBytes()); + } else { + // when the directory with the same content.hashcode() already exists, check its content as + // well. + byte[] bytes = Files.readAllBytes(srcFile); + if (!new String(bytes).equals(fileContent)) { + // only write when sth actually changed + Files.write(srcFile, fileContent.getBytes()); + } + } + + /* TODO: don't save source as file - make use of JavaFileObjectImpl + int i = name.lastIndexOf('.'); + String packageName = i < 0 ? "" : name.substring(0, i); + String className = i < 0 ? name : name.substring(i + 1); + JavaFileObjectImpl javaFileObject = new JavaFileObjectImpl(className, sourceCode); + javaFileManager.putFileForInput(StandardLocation.SOURCE_PATH, packageName, + className + ClassUtils.JAVA_EXTENSION, javaFileObject); + */ + + return compile(Collections.singletonList(srcFile)); + + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Nonnull + static Path compile(List srcFiles) { + + // create key for temp dir / caching + StringBuilder sb = new StringBuilder(); + for (Path srcFile : srcFiles) { + sb.append(srcFile); + } + String concatenatedFileNames = sb.toString(); + + try { + Path binDirpath = getTempDirectory(concatenatedFileNames).resolve("bin/"); + File binDir = binDirpath.toFile(); + boolean binDirCreated = binDir.mkdirs(); + if (!binDirCreated) { + // bin dir already exists -> check modified time + FileTime binDirLastModifiedTime = Files.getLastModifiedTime(binDirpath); + boolean cacheDirty = false; + for (Path srcFile : srcFiles) { + if (Files.getLastModifiedTime(srcFile).compareTo(binDirLastModifiedTime) > 0) { + cacheDirty = true; + } + } + if (!cacheDirty) { + return binDirpath; + } + } + + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null); + fileManager.setLocation(StandardLocation.CLASS_OUTPUT, Collections.singleton(binDir)); + + File[] files = new File[srcFiles.size()]; + srcFiles.stream().map(Path::toFile).collect(Collectors.toList()).toArray(files); + Iterable javaFileObjects = fileManager.getJavaFileObjects(files); + + try (Writer writer = new StringWriter()) { + JavaCompiler.CompilationTask task = + compiler.getTask(writer, fileManager, null, null, null, javaFileObjects); + + if (task.call()) { + /* collect all generated .class files + Set clazzType = Collections.singleton(JavaFileObject.Kind.CLASS); + for (JavaFileObject jfo : fileManager.list(location, "", clazzType, true)) { + compiledFiles.add(Paths.get(jfo.toUri())); + }*/ + if (!binDirCreated) { + // update modified timestamp of bin/ + Files.setLastModifiedTime(binDirpath, FileTime.fromMillis(currentTimeMillis())); + } + return binDir.toPath(); + } else { + throw new IllegalArgumentException("Could not compile the given input.\n " + writer); + } + } + + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/RuntimeJarConversionTests.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/RuntimeJarConversionTests.java index 6180d3a228d..ff256e051d8 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/RuntimeJarConversionTests.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/RuntimeJarConversionTests.java @@ -6,7 +6,7 @@ import java.util.Collections; import java.util.List; import java.util.function.BiFunction; -import jdk.nashorn.internal.ir.annotations.Ignore; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import sootup.core.inputlocation.AnalysisInputLocation; @@ -116,7 +116,7 @@ private static Body convertMethod(String methodSignature, AnalysisInputLocation return sootMethod.getBody(); } - @Ignore + @Disabled @Test public void testExample() { /* Example to start quickly */ diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/frontend/JavaModuleViewTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/frontend/JavaModuleViewTest.java index e4a735d2d53..90187010f30 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/frontend/JavaModuleViewTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/frontend/JavaModuleViewTest.java @@ -17,10 +17,7 @@ import sootup.java.bytecode.inputlocation.JavaClassPathAnalysisInputLocation; import sootup.java.bytecode.inputlocation.JavaModulePathAnalysisInputLocation; import sootup.java.bytecode.inputlocation.JrtFileSystemAnalysisInputLocation; -import sootup.java.core.JavaModuleIdentifierFactory; -import sootup.java.core.JavaModuleInfo; -import sootup.java.core.JavaSootClass; -import sootup.java.core.ModuleInfoAnalysisInputLocation; +import sootup.java.core.*; import sootup.java.core.signatures.ModulePackageName; import sootup.java.core.types.ModuleJavaClassType; import sootup.java.core.views.JavaModuleView; diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/inputlocation/OTFCompileAnalysisInputLocationTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/inputlocation/OTFCompileAnalysisInputLocationTest.java new file mode 100644 index 00000000000..9338599bb1a --- /dev/null +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/inputlocation/OTFCompileAnalysisInputLocationTest.java @@ -0,0 +1,97 @@ +package sootup.java.bytecode.inputlocation; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Optional; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import sootup.java.core.JavaSootClass; +import sootup.java.core.views.JavaView; + +class OTFCompileAnalysisInputLocationTest { + + @Test + void testSimpleString() { + String cucontent = "public class A { }\n"; + OTFCompileAnalysisInputLocation inputLocation = + new OTFCompileAnalysisInputLocation("A.java", cucontent); + JavaView javaView = new JavaView(inputLocation); + Optional aClass = + javaView.getClass(javaView.getIdentifierFactory().getClassType("A")); + Assertions.assertTrue(aClass.isPresent()); + } + + @Test + void testMismatchingString() { + // should fail + String fileName = "B.java"; + String cucontent = "public class A { }\n"; + assertThrows( + IllegalArgumentException.class, + () -> new OTFCompileAnalysisInputLocation(fileName, cucontent)); + } + + @Test + void testString() { + String cucontent = + "public class FieldAssignment {\n" + + " private static class A {\n" + + " String s;\n" + + " }\n" + + "\n" + + " public static void entry() {\n" + + " A a = new A();\n" + + " String b = \"abc\";\n" + + "\n" + + " a.s = b;\n" + + " }\n" + + "}\n"; + OTFCompileAnalysisInputLocation inputLocation = + new OTFCompileAnalysisInputLocation("FieldAssignment.java", cucontent); + JavaView javaView = new JavaView(inputLocation); + Optional aClass = + javaView.getClass(javaView.getIdentifierFactory().getClassType("FieldAssignment")); + Assertions.assertTrue(aClass.isPresent()); + + Optional aInnerClass = + javaView.getClass(javaView.getIdentifierFactory().getClassType("FieldAssignment$A")); + Assertions.assertTrue(aInnerClass.isPresent()); + } + + @Test + void testSingleInputfile() { + String str = "../shared-test-resources/TypeResolverTestSuite/Misc/FieldAssignment.java"; + OTFCompileAnalysisInputLocation inputLocation = + new OTFCompileAnalysisInputLocation(Paths.get(str)); + JavaView javaView = new JavaView(inputLocation); + Optional aClass = + javaView.getClass(javaView.getIdentifierFactory().getClassType("FieldAssignment")); + Assertions.assertTrue(aClass.isPresent()); + + Optional aInnerClass = + javaView.getClass(javaView.getIdentifierFactory().getClassType("FieldAssignment$A")); + Assertions.assertTrue(aInnerClass.isPresent()); + } + + @Test + void testMultipleDependedFiles() { + Path super2 = + Paths.get("../shared-test-resources/TypeResolverTestSuite/CastCounterTest/Super2.java"); + Path sub2 = + Paths.get("../shared-test-resources/TypeResolverTestSuite/CastCounterTest/Sub2.java"); + + OTFCompileAnalysisInputLocation inputLocation = + new OTFCompileAnalysisInputLocation(Arrays.asList(super2, sub2)); + JavaView javaView = new JavaView(inputLocation); + Optional super2Class = + javaView.getClass(javaView.getIdentifierFactory().getClassType("Super2")); + Assertions.assertTrue(super2Class.isPresent()); + + Optional sub2Class = + javaView.getClass(javaView.getIdentifierFactory().getClassType("Sub2")); + Assertions.assertTrue(sub2Class.isPresent()); + } +} diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/DeadAssignmentEliminatorTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/DeadAssignmentEliminatorTest.java index dc5c01cacd3..2407412259f 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/DeadAssignmentEliminatorTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/DeadAssignmentEliminatorTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.*; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import sootup.core.graph.MutableStmtGraph; import sootup.core.graph.StmtGraph; @@ -22,6 +23,7 @@ import sootup.java.core.types.JavaClassType; import sootup.java.core.views.JavaView; +@Tag("Java8") public class DeadAssignmentEliminatorTest { /** diff --git a/sootup.java.core/pom.xml b/sootup.java.core/pom.xml index 28486bc021b..838555b0a42 100644 --- a/sootup.java.core/pom.xml +++ b/sootup.java.core/pom.xml @@ -9,7 +9,7 @@ org.soot-oss sootup - 1.2.1-SNAPSHOT + 1.3.1-SNAPSHOT diff --git a/sootup.java.core/src/main/java/sootup/java/core/interceptors/typeresolving/TypeResolver.java b/sootup.java.core/src/main/java/sootup/java/core/interceptors/typeresolving/TypeResolver.java index d1f3be14340..060fa349433 100644 --- a/sootup.java.core/src/main/java/sootup/java/core/interceptors/typeresolving/TypeResolver.java +++ b/sootup.java.core/src/main/java/sootup/java/core/interceptors/typeresolving/TypeResolver.java @@ -37,6 +37,7 @@ import sootup.core.jimple.common.expr.JCastExpr; import sootup.core.jimple.common.expr.JNegExpr; import sootup.core.jimple.common.ref.JArrayRef; +import sootup.core.jimple.common.ref.JInstanceFieldRef; import sootup.core.jimple.common.stmt.AbstractDefinitionStmt; import sootup.core.jimple.common.stmt.Stmt; import sootup.core.model.Body; @@ -201,6 +202,9 @@ private Collection applyAssignmentConstraint( local = (Local) lhs; } else if (lhs instanceof JArrayRef) { local = ((JArrayRef) lhs).getBase(); + } else if (lhs instanceof JInstanceFieldRef) { + // local = ((JInstanceFieldRef) lhs).getBase(); + continue; // assigment to a field is independent of the base type. } else { // Only `Local`s and `JArrayRef`s as the left-hand side are relevant for type inference. // The statements get filtered to only contain those assignments in the `init` method, diff --git a/sootup.java.sourcecode/pom.xml b/sootup.java.sourcecode/pom.xml index c7ac6683f1c..b14ac745301 100644 --- a/sootup.java.sourcecode/pom.xml +++ b/sootup.java.sourcecode/pom.xml @@ -9,7 +9,7 @@ org.soot-oss sootup - 1.2.1-SNAPSHOT + 1.3.1-SNAPSHOT diff --git a/sootup.jimple.parser/pom.xml b/sootup.jimple.parser/pom.xml index 394f432bee3..0fb1260e12e 100644 --- a/sootup.jimple.parser/pom.xml +++ b/sootup.jimple.parser/pom.xml @@ -10,7 +10,7 @@ org.soot-oss sootup - 1.2.1-SNAPSHOT + 1.3.1-SNAPSHOT diff --git a/sootup.qilin/pom.xml b/sootup.qilin/pom.xml new file mode 100644 index 00000000000..31556a65a5f --- /dev/null +++ b/sootup.qilin/pom.xml @@ -0,0 +1,98 @@ + + + 4.0.0 + + SootUp Qilin + sootup.qilin + + + org.soot-oss + sootup + 1.3.1-SNAPSHOT + + + + + org.soot-oss + sootup.core + ${project.version} + + + org.soot-oss + sootup.java.core + ${project.version} + + + org.soot-oss + sootup.java.bytecode + ${project.version} + + + org.soot-oss + sootup.callgraph + ${project.version} + + + + commons-cli + commons-cli + 1.5.0 + + + + com.github.oshi + oshi-core + 6.4.0 + + + + de.upb.cs.swt + heros + 1.2.3 + + + junit + junit + 4.13.1 + test + + + + + + + + + maven-assembly-plugin + + + + driver.Main + + + + jar-with-dependencies + + + + + + + \ No newline at end of file diff --git a/sootup.qilin/run.sh b/sootup.qilin/run.sh new file mode 100755 index 00000000000..32c9fec787d --- /dev/null +++ b/sootup.qilin/run.sh @@ -0,0 +1,3 @@ +#!/bin/sh +mvn clean compile assembly:single +mv target/sootup.qilin-1.3.1-SNAPSHOT-jar-with-dependencies.jar ../artifact/Qilin-0.9.4-SNAPSHOT.jar diff --git a/sootup.qilin/src/main/java/qilin/.DS_Store b/sootup.qilin/src/main/java/qilin/.DS_Store new file mode 100644 index 00000000000..5fe65c9d9a0 Binary files /dev/null and b/sootup.qilin/src/main/java/qilin/.DS_Store differ diff --git a/sootup.qilin/src/main/java/qilin/CoreConfig.java b/sootup.qilin/src/main/java/qilin/CoreConfig.java new file mode 100644 index 00000000000..0abdadebfb3 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/CoreConfig.java @@ -0,0 +1,143 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin; + +import java.util.List; + +public class CoreConfig { + protected static CoreConfig coreConfig = null; + + public static CoreConfig v() { + if (coreConfig == null) { + throw new RuntimeException("Core configuration is not initialized!"); + } + return coreConfig; + } + + public static void reset() { + coreConfig = null; + } + + public enum ClinitMode { + FULL, + ONFLY, + APP + } + + public static class CorePTAConfiguration { + public boolean singleentry = false; + + /** + * Clinit loading mode: APP: A debug mode for testing which load only the minimum <clinit> + * of classes needed. FULL: load all <clinit> of classes that are currently loaded in the + * scene. ON_THE_FLY: we load <clinit> on the fly, this mode is same as DOOP (default + * mode). + */ + public ClinitMode clinitMode = ClinitMode.ONFLY; + + /** enable to merge heaps like StringBuilder/StringBuffer, exceptions by types. */ + public boolean mergeHeap = false; + + /** + * the type of array element is java.lang.Object by default. if set to be true, we will use more + * precise type for array elements. + */ + public boolean preciseArrayElement = false; + + /** + * in qilin.spark propagate all string constants false means merge all string constants, see the + * corresponding flag, DISTINGUISH_NO_STRING_CONSTANTS, in DOOP. + */ + public boolean stringConstants = false; + + /** + * The default setting (same as the imprecise version in Doop): there is a global variable for + * all thrown exceptions and any caught exception variable points to all those exceptions. + * Obviously, this is extremely imprecise. + * + *

This imprecision does not seem to matter too much for typical programs in the + * context-insensitive and 1-call-site-sensitive analyses, but is disastrous in the + * 1-object-sensitive analysis. + * + *

once this switch is open, it will model exception flow-sensitively (same as the precise + * version in Doop). Implicitly thrown exceptions are not included. + */ + public boolean preciseExceptions = false; + + /** in qilin.spark limit heap context for strings if we are object sensitive */ + public boolean enforceEmptyCtxForIgnoreTypes = false; + + public String ptaName; + } + + public static class ApplicationConfiguration { + /** Path for the root folder for the application classes or for the application jar file. */ + public String APP_PATH = "."; + + /** Path for the JRE to be used for whole program analysis. */ + public String JRE = null; + + /** Path for the root folder for the library jars. */ + public String LIB_PATH = null; + + /** Path for the reflection log file for the application. */ + public String REFLECTION_LOG = null; + + /** Main class for the application. */ + public String MAIN_CLASS = null; + + /** exclude selected packages */ + public List EXCLUDE = null; + + /** include packages which are not analyzed by default */ + public boolean INCLUDE_ALL = false; + } + + public static class OutputConfiguration { + public String outDir = ""; + + /** dump appclasses to jimple */ + public boolean dumpJimple = false; + + /** if true, dump pts in app code to a file */ + public boolean dumppts = false; + + /** if true, dump pts of vars in library */ + public boolean dumplibpts = false; + + /** if true, dump stats into files. */ + public boolean dumpStats = false; + } + + protected CorePTAConfiguration ptaConfig; + protected final ApplicationConfiguration appConfig = new ApplicationConfiguration(); + protected final OutputConfiguration outConfig = new OutputConfiguration(); + + public CorePTAConfiguration getPtaConfig() { + return ptaConfig; + } + + public ApplicationConfiguration getAppConfig() { + return appConfig; + } + + public OutputConfiguration getOutConfig() { + return outConfig; + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/ArtificialMethod.java b/sootup.qilin/src/main/java/qilin/core/ArtificialMethod.java new file mode 100644 index 00000000000..1d2537b09f8 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/ArtificialMethod.java @@ -0,0 +1,195 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import qilin.util.PTAUtils; +import sootup.core.jimple.Jimple; +import sootup.core.jimple.basic.*; +import sootup.core.jimple.common.constant.IntConstant; +import sootup.core.jimple.common.expr.AbstractInvokeExpr; +import sootup.core.jimple.common.expr.JNewExpr; +import sootup.core.jimple.common.ref.*; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.Body; +import sootup.core.model.SootClass; +import sootup.core.model.SootField; +import sootup.core.model.SootMethod; +import sootup.core.signatures.MethodSignature; +import sootup.core.types.ArrayType; +import sootup.core.types.ClassType; +import sootup.core.types.Type; +import sootup.core.views.View; +import sootup.java.core.JavaIdentifierFactory; +import sootup.java.core.language.JavaJimple; + +public abstract class ArtificialMethod { + protected final View view; + protected SootMethod method; + protected Body.BodyBuilder bodyBuilder; + protected Local thisLocal; + protected Local[] paraLocals; + protected int paraStart; + protected int localStart; + protected final List stmtList; + + protected ArtificialMethod(View view) { + this.view = view; + this.stmtList = new ArrayList<>(); + } + + protected Local getThis() { + if (thisLocal == null) { + ClassType type = method.getDeclaringClassType(); + IdentityRef thisRef = new JThisRef(type); + thisLocal = getLocal(type, 0); + addIdentity(thisLocal, thisRef); + } + return thisLocal; + } + + protected Local getPara(int index) { + Local paraLocal = paraLocals[index]; + if (paraLocal == null) { + Type type = method.getParameterType(index); + return getPara(index, type); + } + return paraLocal; + } + + protected Local getPara(int index, Type type) { + Local paraLocal = paraLocals[index]; + if (paraLocal == null) { + IdentityRef paraRef = new JParameterRef(type, index); + paraLocal = getLocal(type, paraStart + index); + addIdentity(paraLocal, paraRef); + paraLocals[index] = paraLocal; + } + return paraLocal; + } + + private void addIdentity(Local lValue, IdentityRef rValue) { + Stmt identityStmt = + Jimple.newIdentityStmt(lValue, rValue, StmtPositionInfo.getNoStmtPositionInfo()); + stmtList.add(identityStmt); + } + + protected Local getNew(ClassType type) { + Value newExpr = new JNewExpr(type); + Local local = getNextLocal(type); + addAssign(local, newExpr); + return local; + } + + protected Local getNewArray(ClassType type) { + Value newExpr = JavaJimple.getInstance().newNewArrayExpr(type, IntConstant.getInstance(1)); + Local local = getNextLocal(new ArrayType(type, 1)); + addAssign(local, newExpr); + return local; + } + + protected Local getNextLocal(Type type) { + return getLocal(type, localStart++); + } + + private Local getLocal(Type type, int index) { + Local local = Jimple.newLocal("r" + index, type); + bodyBuilder.addLocal(local); + return local; + } + + protected void addReturn(Immediate ret) { + Stmt stmt = Jimple.newReturnStmt(ret, StmtPositionInfo.getNoStmtPositionInfo()); + stmtList.add(stmt); + } + + protected JStaticFieldRef getStaticFieldRef(String className, String name) { + ClassType classType = PTAUtils.getClassType(className); + SootClass sc = (SootClass) view.getClass(classType).get(); + SootField field = (SootField) sc.getField(name).get(); + return Jimple.newStaticFieldRef(field.getSignature()); + } + + protected JArrayRef getArrayRef(Value base) { + return JavaJimple.getInstance().newArrayRef((Local) base, IntConstant.getInstance(0)); + } + + /** add an instance invocation receiver.sig(args) */ + protected void addInvoke(Local receiver, String sig, Immediate... args) { + MethodSignature methodSig = JavaIdentifierFactory.getInstance().parseMethodSignature(sig); + SootMethod method = (SootMethod) view.getMethod(methodSig).get(); + SootClass clazz = (SootClass) view.getClass(method.getDeclaringClassType()).get(); + List argsL = Arrays.asList(args); + AbstractInvokeExpr invoke = + clazz.isInterface() + ? Jimple.newInterfaceInvokeExpr(receiver, methodSig, argsL) + : Jimple.newVirtualInvokeExpr(receiver, methodSig, argsL); + Stmt stmt = Jimple.newInvokeStmt(invoke, StmtPositionInfo.getNoStmtPositionInfo()); + stmtList.add(stmt); + } + + /** + * add an instance invocation and get the return value rx = receiver.sig(args) + * + * @return rx + */ + protected Local getInvoke(Local receiver, String sig, Immediate... args) { + MethodSignature methodSig = JavaIdentifierFactory.getInstance().parseMethodSignature(sig); + SootMethod method = (SootMethod) view.getMethod(methodSig).get(); + SootClass clazz = (SootClass) view.getClass(method.getDeclaringClassType()).get(); + List argsL = Arrays.asList(args); + Value invoke = + clazz.isInterface() + ? Jimple.newInterfaceInvokeExpr(receiver, methodSig, argsL) + : Jimple.newVirtualInvokeExpr(receiver, methodSig, argsL); + Local rx = getNextLocal(method.getReturnType()); + addAssign(rx, invoke); + return rx; + } + + /** add a static invocation sig(args) */ + protected void addInvoke(String sig, Immediate... args) { + MethodSignature methodSig = JavaIdentifierFactory.getInstance().parseMethodSignature(sig); + List argsL = Arrays.asList(args); + Stmt stmt = + Jimple.newInvokeStmt( + Jimple.newStaticInvokeExpr(methodSig, argsL), StmtPositionInfo.getNoStmtPositionInfo()); + stmtList.add(stmt); + } + + /** + * add a static invocation and get the return value rx = sig(args) + * + * @return rx + */ + protected Value getInvoke(String sig, Immediate... args) { + MethodSignature methodSig = JavaIdentifierFactory.getInstance().parseMethodSignature(sig); + List argsL = Arrays.asList(args); + LValue rx = getNextLocal(methodSig.getType()); + addAssign(rx, Jimple.newStaticInvokeExpr(methodSig, argsL)); + return rx; + } + + protected void addAssign(LValue lValue, Value rValue) { + Stmt stmt = Jimple.newAssignStmt(lValue, rValue, StmtPositionInfo.getNoStmtPositionInfo()); + stmtList.add(stmt); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/CorePTA.java b/sootup.qilin/src/main/java/qilin/core/CorePTA.java new file mode 100644 index 00000000000..5cae3650ae4 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/CorePTA.java @@ -0,0 +1,129 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core; + +import qilin.core.context.Context; +import qilin.core.pag.*; +import qilin.core.solver.Propagator; +import qilin.parm.ctxcons.CtxConstructor; +import qilin.parm.heapabst.HeapAbstractor; +import qilin.parm.select.CtxSelector; +import sootup.core.model.SootMethod; + +/* + * This represents a parameterized PTA which could be concreted to many pointer analyses. + * */ +public abstract class CorePTA extends PTA { + /* + * The following three parameterized functions must be initialized before doing the pointer analysis. + * */ + protected CtxConstructor ctxCons; + protected CtxSelector ctxSel; + protected HeapAbstractor heapAbst; + + public CorePTA(PTAScene scene) { + super(scene); + } + + public CtxSelector ctxSelector() { + return ctxSel; + } + + public void setContextSelector(CtxSelector ctxSelector) { + this.ctxSel = ctxSelector; + } + + public CtxConstructor ctxConstructor() { + return ctxCons; + } + + public HeapAbstractor heapAbstractor() { + return this.heapAbst; + } + + public abstract Propagator getPropagator(); + + @Override + public Context createCalleeCtx( + ContextMethod caller, AllocNode receiverNode, CallSite callSite, SootMethod target) { + return ctxCons.constructCtx(caller, (ContextAllocNode) receiverNode, callSite, target); + } + + public Context emptyContext() { + return CtxConstructor.emptyContext; + } + + @Override + public Node parameterize(Node n, Context context) { + if (context == null) { + throw new RuntimeException("null context!!!"); + } + if (n instanceof LocalVarNode) { + LocalVarNode lvn = (LocalVarNode) n; + return parameterize(lvn, context); + } + if (n instanceof FieldRefNode) { + FieldRefNode frn = (FieldRefNode) n; + return parameterize(frn, context); + } + if (n instanceof AllocNode) { + AllocNode an = (AllocNode) n; + return parameterize(an, context); + } + if (n instanceof FieldValNode) { + FieldValNode fvn = (FieldValNode) n; + return parameterize(fvn, context); + } + if (n instanceof GlobalVarNode) { + GlobalVarNode gvn = (GlobalVarNode) n; + return pag.makeContextVarNode(gvn, emptyContext()); + } + throw new RuntimeException("cannot parameterize this node: " + n); + } + + public ContextField parameterize(FieldValNode fvn, Context context) { + Context ctx = ctxSel.select(fvn, context); + return pag.makeContextField(ctx, fvn); + } + + protected ContextVarNode parameterize(LocalVarNode vn, Context context) { + Context ctx = ctxSel.select(vn, context); + return pag.makeContextVarNode(vn, ctx); + } + + protected FieldRefNode parameterize(FieldRefNode frn, Context context) { + return pag.makeFieldRefNode((VarNode) parameterize(frn.getBase(), context), frn.getField()); + } + + protected ContextAllocNode parameterize(AllocNode node, Context context) { + Context ctx = ctxSel.select(node, context); + return pag.makeContextAllocNode(node, ctx); + } + + /** Finds or creates the ContextMethod for method and context. */ + @Override + public ContextMethod parameterize(SootMethod method, Context context) { + Context ctx = ctxSel.select(method, context); + return pag.makeContextMethod(ctx, method); + } + + public AllocNode getRootNode() { + return rootNode; + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/PTA.java b/sootup.qilin/src/main/java/qilin/core/PTA.java new file mode 100644 index 00000000000..041d22f4c3e --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/PTA.java @@ -0,0 +1,269 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import qilin.core.builder.CallGraphBuilder; +import qilin.core.builder.ExceptionHandler; +import qilin.core.builder.callgraph.OnFlyCallGraph; +import qilin.core.context.Context; +import qilin.core.pag.*; +import qilin.core.sets.*; +import qilin.core.solver.Propagator; +import qilin.parm.ctxcons.CtxConstructor; +import qilin.parm.heapabst.HeapAbstractor; +import qilin.parm.select.CtxSelector; +import qilin.util.PTAUtils; +import sootup.core.jimple.basic.Local; +import sootup.core.model.SootField; +import sootup.core.model.SootMethod; +import sootup.core.views.View; + +public abstract class PTA implements PointsToAnalysis { + protected PTAScene scene; + protected AllocNode rootNode; + protected PAG pag; + protected OnFlyCallGraph callGraph; + protected CallGraphBuilder cgb; + protected ExceptionHandler eh; + + public PTA(PTAScene scene) { + this.scene = scene; + this.pag = createPAG(); + this.cgb = createCallGraphBuilder(); + this.eh = new ExceptionHandler(this); + AllocNode rootBase = pag.makeAllocNode("ROOT", PTAUtils.getClassType("java.lang.Object"), null); + this.rootNode = new ContextAllocNode(rootBase, CtxConstructor.emptyContext); + } + + protected abstract PAG createPAG(); + + protected abstract CallGraphBuilder createCallGraphBuilder(); + + public void run() { + pureRun(); + } + + public void pureRun() { + getPropagator().propagate(); + } + + public PAG getPag() { + return pag; + } + + public View getView() { + return scene.getView(); + } + + public PTAScene getScene() { + return scene; + } + + public CallGraphBuilder getCgb() { + return cgb; + } + + public ExceptionHandler getExceptionHandler() { + return eh; + } + + public OnFlyCallGraph getCallGraph() { + if (callGraph == null) { + callGraph = cgb.getCICallGraph(); + } + return callGraph; + } + + public Collection getReachableMethods() { + return cgb.getReachableMethods(); + } + + private Set nakedReachables = null; + + public Collection getNakedReachableMethods() { + if (nakedReachables == null) { + nakedReachables = new HashSet<>(); + cgb.getReachableMethods().forEach(momc -> nakedReachables.add(momc.method())); + } + return nakedReachables; + } + + protected abstract Propagator getPropagator(); + + public abstract Node parameterize(Node n, Context context); + + public abstract ContextMethod parameterize(SootMethod method, Context context); + + public abstract AllocNode getRootNode(); + + public abstract Context emptyContext(); + + public abstract Context createCalleeCtx( + ContextMethod caller, AllocNode receiverNode, CallSite callSite, SootMethod target); + + public abstract HeapAbstractor heapAbstractor(); + + public abstract CtxConstructor ctxConstructor(); + + public abstract CtxSelector ctxSelector(); + + /** Returns the set of objects pointed to by variable l. */ + @Override + public PointsToSet reachingObjects(SootMethod m, Local l) { + // find all context nodes, and collect their answers + final PointsToSetInternal ret = new HybridPointsToSet(); + pag.getVarNodes(m, l) + .forEach( + vn -> { + ret.addAll(vn.getP2Set(), null); + }); + return new UnmodifiablePointsToSet(this, ret); + } + + /** + * Returns the set of objects pointed by n: case 1: n is an insensitive node, return objects + * pointed by n under every possible context. case 2: n is a context-sensitive node, return + * objects pointed by n under the given context. + */ + public PointsToSet reachingObjects(Node n) { + final PointsToSetInternal ret; + if (n instanceof ContextVarNode) { + ContextVarNode cvn = (ContextVarNode) n; + ret = cvn.getP2Set(); + } else if (n instanceof ContextField) { + ContextField cf = (ContextField) n; + ret = cf.getP2Set(); + } else { + VarNode varNode = (VarNode) n; + ret = new HybridPointsToSet(); + if (pag.getContextVarNodeMap().containsKey(varNode)) { + pag.getContextVarNodeMap() + .get(varNode) + .values() + .forEach( + vn -> { + ret.addAll(vn.getP2Set(), null); + }); + } + } + return new UnmodifiablePointsToSet(this, ret); + } + + /** Returns the set of objects pointed to by elements of the arrays in the PointsToSet s. */ + @Override + public PointsToSet reachingObjectsOfArrayElement(PointsToSet s) { + return reachingObjectsInternal(s, ArrayElement.v()); + } + + /** Returns the set of objects pointed to by variable l in context c. */ + @Override + public PointsToSet reachingObjects(Context c, SootMethod m, Local l) { + VarNode n = pag.findContextVarNode(m, l, c); + PointsToSetInternal pts; + if (n == null) { + pts = HybridPointsToSet.getEmptySet(); + } else { + pts = n.getP2Set(); + } + return new UnmodifiablePointsToSet(this, pts); + } + + /** Returns the set of objects pointed to by instance field f of the objects pointed to by l. */ + @Override + public PointsToSet reachingObjects(SootMethod m, Local l, SootField f) { + return reachingObjects(reachingObjects(m, l), f); + } + + /** + * Returns the set of objects pointed to by instance field f of the objects in the PointsToSet s. + */ + @Override + public PointsToSet reachingObjects(PointsToSet s, final SootField f) { + if (f.isStatic()) { + throw new RuntimeException("The parameter f must be an *instance* field."); + } + return reachingObjectsInternal(s, new Field(f)); + } + + /** + * Returns the set of objects pointed to by instance field f of the objects pointed to by l in + * context c. + */ + @Override + public PointsToSet reachingObjects(Context c, SootMethod m, Local l, SootField f) { + return reachingObjects(reachingObjects(c, m, l), f); + } + + @Override + public PointsToSet reachingObjects(SootField f) { + PointsToSetInternal ret; + if (f.isStatic()) { + VarNode n = pag.findGlobalVarNode(f); + if (n == null) { + ret = HybridPointsToSet.getEmptySet(); + } else { + ret = n.getP2Set(); + } + } else { + ret = new HybridPointsToSet(); + SparkField sparkField = new Field(f); + pag.getContextFieldVarNodeMap().values().stream() + .filter(map -> map.containsKey(sparkField)) + .forEach( + map -> { + ContextField contextField = map.get(sparkField); + ret.addAll(contextField.getP2Set(), null); + }); + } + return new UnmodifiablePointsToSet(this, ret); + } + + public PointsToSet reachingObjectsInternal(PointsToSet bases, final SparkField f) { + final PointsToSetInternal ret = new HybridPointsToSet(); + pag.getContextFieldVarNodeMap().values().stream() + .filter(map -> map.containsKey(f)) + .forEach( + map -> { + ContextField contextField = map.get(f); + AllocNode base = contextField.getBase(); + if (bases.contains(base)) { + ret.addAll(contextField.getP2Set(), null); + } + }); + return new UnmodifiablePointsToSet(this, ret); + } + + public PointsToSet reachingObjectsInternal(AllocNode heap, final SparkField f) { + final PointsToSetInternal ret = new HybridPointsToSet(); + pag.getContextFieldVarNodeMap().values().stream() + .filter(map -> map.containsKey(f)) + .forEach( + map -> { + ContextField contextField = map.get(f); + AllocNode base = contextField.getBase(); + if (heap.equals(base)) { + ret.addAll(contextField.getP2Set(), null); + } + }); + return new UnmodifiablePointsToSet(this, ret); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/PTAScene.java b/sootup.qilin/src/main/java/qilin/core/PTAScene.java new file mode 100644 index 00000000000..97fee24201d --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/PTAScene.java @@ -0,0 +1,149 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core; + +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import qilin.core.builder.FakeMainFactory; +import qilin.core.builder.callgraph.OnFlyCallGraph; +import qilin.util.DataFactory; +import qilin.util.PTAUtils; +import sootup.core.jimple.basic.Value; +import sootup.core.jimple.common.ref.JStaticFieldRef; +import sootup.core.model.SootClass; +import sootup.core.model.SootField; +import sootup.core.model.SootMethod; +import sootup.core.signatures.FieldSignature; +import sootup.core.signatures.MethodSignature; +import sootup.core.types.ClassType; +import sootup.core.views.View; +import sootup.java.core.JavaIdentifierFactory; + +public class PTAScene { + private final View view; + private OnFlyCallGraph callgraph; + private final FakeMainFactory fakeMainFactory; + + public final Set nativeBuilt; + public final Set reflectionBuilt; + public final Set arraycopyBuilt; + + public PTAScene(View view, String mainClassSig) { + this.nativeBuilt = DataFactory.createSet(); + this.reflectionBuilt = DataFactory.createSet(); + this.arraycopyBuilt = DataFactory.createSet(); + this.view = view; + SootClass mainClass = getSootClass(mainClassSig); + // setup fakemain + this.fakeMainFactory = new FakeMainFactory(view, mainClass); + } + + /* + * wrapper methods for FakeMain. + * */ + public SootMethod getFakeMainMethod() { + return this.fakeMainFactory.getFakeMain(); + } + + public JStaticFieldRef getFieldCurrentThread() { + return this.fakeMainFactory.getFieldCurrentThread(); + } + + public Value getFieldGlobalThrow() { + return this.fakeMainFactory.getFieldGlobalThrow(); + } + + /* + * wrapper methods of Soot Scene. Note, we do not allow you to use Soot Scene directly in qilin.qilin.pta subproject + * to avoid confusing. + * */ + public void setCallGraph(OnFlyCallGraph cg) { + this.callgraph = cg; + } + + public View getView() { + return view; + } + + public OnFlyCallGraph getCallGraph() { + return this.callgraph; + } + + public SootMethod getMethod(String methodSignature) { + MethodSignature mthdSig = + JavaIdentifierFactory.getInstance().parseMethodSignature(methodSignature); + return view.getMethod(mthdSig).get(); + } + + public Collection getApplicationClasses() { + Collection classes = view.getClasses(); + return classes.stream().filter(SootClass::isApplicationClass).collect(Collectors.toSet()); + } + + public Collection getLibraryClasses() { + Collection classes = view.getClasses(); + return classes.stream().filter(SootClass::isLibraryClass).collect(Collectors.toSet()); + } + + public boolean containsMethod(String methodSignature) { + MethodSignature mthdSig = + JavaIdentifierFactory.getInstance().parseMethodSignature(methodSignature); + return view.getMethod(mthdSig).isPresent(); + } + + public boolean containsField(String fieldSignature) { + FieldSignature fieldSig = + JavaIdentifierFactory.getInstance().parseFieldSignature(fieldSignature); + return view.getField(fieldSig).isPresent(); + } + + public Collection getClasses() { + return view.getClasses(); + } + + public Collection getPhantomClasses() { + return Collections.emptySet(); + } + + public SootClass getSootClass(String className) { + ClassType classType = PTAUtils.getClassType(className); + return view.getClass(classType).get(); + } + + public boolean containsClass(String className) { + ClassType classType = PTAUtils.getClassType(className); + Optional oclazz = view.getClass(classType); + return oclazz.isPresent(); + } + + public SootField getField(String fieldSignature) { + FieldSignature fieldSig = + JavaIdentifierFactory.getInstance().parseFieldSignature(fieldSignature); + return view.getField(fieldSig).get(); + } + + public boolean isApplicationMethod(SootMethod sm) { + ClassType classType = sm.getDeclaringClassType(); + Optional osc = view.getClass(classType); + return osc.map(SootClass::isApplicationClass).orElse(false); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/PointsToAnalysis.java b/sootup.qilin/src/main/java/qilin/core/PointsToAnalysis.java new file mode 100644 index 00000000000..a4437d0d586 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/PointsToAnalysis.java @@ -0,0 +1,69 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core; + +import qilin.core.context.Context; +import qilin.core.pag.Node; +import qilin.core.sets.PointsToSet; +import sootup.core.jimple.basic.Local; +import sootup.core.model.SootField; +import sootup.core.model.SootMethod; + +/** + * A generic interface to any type of pointer analysis. + * + * @author Ondrej Lhotak + */ +public interface PointsToAnalysis { + + int THIS_NODE = -1; + int RETURN_NODE = -2; + int THROW_NODE = -3; + String STRING_NODE = "STRING_NODE"; + String EXCEPTION_NODE = "EXCEPTION_NODE"; + String MAIN_THREAD_GROUP_NODE_LOCAL = "MAIN_THREAD_GROUP_NODE_LOCAL"; + + /** Returns the set of objects pointed to by variable l. */ + PointsToSet reachingObjects(SootMethod m, Local l); + + PointsToSet reachingObjects(Node n); + + /** Returns the set of objects pointed to by variable l in context c. */ + PointsToSet reachingObjects(Context c, SootMethod m, Local l); + + /** Returns the set of objects pointed to by static field f. */ + PointsToSet reachingObjects(SootField f); + + /** + * Returns the set of objects pointed to by instance field f of the objects in the PointsToSet s. + */ + PointsToSet reachingObjects(PointsToSet s, SootField f); + + /** Returns the set of objects pointed to by instance field f of the objects pointed to by l. */ + PointsToSet reachingObjects(SootMethod m, Local l, SootField f); + + /** + * Returns the set of objects pointed to by instance field f of the objects pointed to by l in + * context c. + */ + PointsToSet reachingObjects(Context c, SootMethod m, Local l, SootField f); + + /** Returns the set of objects pointed to by elements of the arrays in the PointsToSet s. */ + PointsToSet reachingObjectsOfArrayElement(PointsToSet s); +} diff --git a/sootup.qilin/src/main/java/qilin/core/VirtualCalls.java b/sootup.qilin/src/main/java/qilin/core/VirtualCalls.java new file mode 100644 index 00000000000..ac4d39f5618 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/VirtualCalls.java @@ -0,0 +1,190 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core; + +import java.util.*; +import qilin.util.DataFactory; +import qilin.util.PTAUtils; +import qilin.util.queue.ChunkedQueue; +import sootup.core.jimple.common.expr.JSpecialInvokeExpr; +import sootup.core.model.SootClass; +import sootup.core.model.SootMethod; +import sootup.core.signatures.MethodSignature; +import sootup.core.signatures.MethodSubSignature; +import sootup.core.types.ArrayType; +import sootup.core.types.ClassType; +import sootup.core.types.NullType; +import sootup.core.types.Type; +import sootup.core.views.View; + +/** + * Resolves virtual calls. + * + * @author Ondrej Lhotak + */ +public class VirtualCalls { + private final Map> typeToVtbl; + protected View view; + + public VirtualCalls(View view) { + this.view = view; + this.typeToVtbl = DataFactory.createMap(view.getClasses().size()); + } + + public SootMethod resolveSpecial( + JSpecialInvokeExpr iie, MethodSubSignature subSig, SootMethod container) { + return resolveSpecial(iie, subSig, container, false); + } + + public SootMethod resolveSpecial( + JSpecialInvokeExpr iie, MethodSubSignature subSig, SootMethod container, boolean appOnly) { + MethodSignature methodSig = iie.getMethodSignature(); + /* cf. JVM spec, invokespecial instruction */ + if (view.getTypeHierarchy() + .isSubtype(methodSig.getDeclClassType(), container.getDeclaringClassType()) + && container.getDeclaringClassType() != methodSig.getDeclClassType() + && !methodSig.getName().equals("") + && !subSig.toString().equals("void ()")) { + SootClass cls = view.getClass(container.getDeclaringClassType()).get(); + ClassType superClsType = cls.getSuperclass().get(); + return resolveNonSpecial(superClsType, subSig, appOnly); + } else { + Optional otgt = view.getMethod(methodSig); + if (!otgt.isPresent()) { + System.out.println( + "Wrarning: signature " + methodSig + " does not have a concrete method."); + } + return otgt.get(); + } + } + + public SootMethod resolveNonSpecial(ClassType t, MethodSubSignature subSig) { + return resolveNonSpecial(t, subSig, false); + } + + public SootMethod resolveNonSpecial(ClassType t, MethodSubSignature subSig, boolean appOnly) { + Map vtbl = + typeToVtbl.computeIfAbsent(t, k -> DataFactory.createMap(8)); + SootMethod ret = vtbl.get(subSig); + if (ret != null) { + return ret; + } + SootClass cls = view.getClass(t).get(); + if (appOnly && cls.isLibraryClass()) { + return null; + } + Optional om = cls.getMethod(subSig); + if (om.isPresent()) { + SootMethod m = om.get(); + if (!m.isAbstract()) { + ret = m; + } + } else { + Optional oc = cls.getSuperclass(); + if (oc.isPresent()) { + ClassType ct = oc.get(); + SootClass c = view.getClass(ct).get(); + ret = resolveNonSpecial(c.getType(), subSig); + } + } + if (ret == null) { + return null; + } + vtbl.put(subSig, ret); + return ret; + } + + public void resolve( + Type t, + Type declaredType, + MethodSubSignature subSig, + SootMethod container, + ChunkedQueue targets) { + resolve(t, declaredType, null, subSig, container, targets); + } + + public void resolve( + Type t, + Type declaredType, + Type sigType, + MethodSubSignature subSig, + SootMethod container, + ChunkedQueue targets) { + resolve(t, declaredType, sigType, subSig, container, targets, false); + } + + public void resolve( + Type t, + Type declaredType, + Type sigType, + MethodSubSignature subSig, + SootMethod container, + ChunkedQueue targets, + boolean appOnly) { + if (declaredType instanceof ArrayType) { + declaredType = PTAUtils.getClassType("java.lang.Object"); + } + if (sigType instanceof ArrayType) { + sigType = PTAUtils.getClassType("java.lang.Object"); + } + if (t instanceof ArrayType) { + t = PTAUtils.getClassType("java.lang.Object"); + } + + if (declaredType != null && !PTAUtils.canStoreType(view, t, declaredType)) { + return; + } + if (sigType != null && !PTAUtils.canStoreType(view, t, sigType)) { + return; + } + if (t instanceof ClassType) { + SootMethod target = resolveNonSpecial((ClassType) t, subSig, appOnly); + if (target != null) { + targets.add(target); + } + } + // else if (t instanceof AnySubType) { + // ClassType base = ((AnySubType) t).getBase(); + // + // /* + // * Whenever any sub type of a specific type is considered as receiver for a method + // to call and the base type is an + // * interface, calls to existing methods with matching signature (possible + // implementation of method to call) are also + // * added. As Javas' subtyping allows contra-variance for return types and + // co-variance for parameters when overriding a + // * method, these cases are also considered here. + // * + // * Example: Classes A, B (B sub type of A), interface I with method public A foo(B + // b); and a class C with method public + // * B foo(A a) { ... }. The extended class hierarchy will contain C as possible + // implementation of I. + // * + // * Since Java has no multiple inheritance call by signature resolution is only + // activated if the base is an interface. + // */ + // resolveAnySubType(declaredType, sigType, subSig, container, targets, appOnly, + // base); + // } + else if (t instanceof NullType) { + } else { + throw new RuntimeException("oops " + t); + } + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/builder/CallGraphBuilder.java b/sootup.qilin/src/main/java/qilin/core/builder/CallGraphBuilder.java new file mode 100644 index 00000000000..b914b53e1af --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/builder/CallGraphBuilder.java @@ -0,0 +1,322 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.builder; + +import java.util.*; +import qilin.CoreConfig; +import qilin.core.PTA; +import qilin.core.PTAScene; +import qilin.core.VirtualCalls; +import qilin.core.builder.callgraph.Edge; +import qilin.core.builder.callgraph.Kind; +import qilin.core.builder.callgraph.OnFlyCallGraph; +import qilin.core.context.Context; +import qilin.core.pag.*; +import qilin.core.sets.P2SetVisitor; +import qilin.core.sets.PointsToSetInternal; +import qilin.util.DataFactory; +import qilin.util.PTAUtils; +import qilin.util.queue.ChunkedQueue; +import qilin.util.queue.QueueReader; +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.basic.StmtPositionInfo; +import sootup.core.jimple.basic.Value; +import sootup.core.jimple.common.constant.NullConstant; +import sootup.core.jimple.common.expr.AbstractInvokeExpr; +import sootup.core.jimple.common.expr.JSpecialInvokeExpr; +import sootup.core.jimple.common.expr.JStaticInvokeExpr; +import sootup.core.jimple.common.stmt.JAssignStmt; +import sootup.core.jimple.common.stmt.JInvokeStmt; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.SootMethod; +import sootup.core.signatures.MethodSubSignature; +import sootup.core.types.ClassType; +import sootup.core.types.ReferenceType; +import sootup.core.types.Type; +import sootup.core.types.UnknownType; + +public class CallGraphBuilder { + private static final ClassType clRunnable = PTAUtils.getClassType("java.lang.Runnable"); + protected final Map> receiverToSites; + protected final Map> methodToInvokeStmt; + protected final Set reachMethods; + private ChunkedQueue rmQueue; + + protected final Set calledges; + protected final PTA pta; + protected final PAG pag; + protected final PTAScene ptaScene; + protected final VirtualCalls virtualCalls; + protected OnFlyCallGraph cicg; + + public CallGraphBuilder(PTA pta) { + this.pta = pta; + this.pag = pta.getPag(); + this.ptaScene = pta.getScene(); + ptaScene.setCallGraph(new OnFlyCallGraph()); + this.virtualCalls = new VirtualCalls(ptaScene.getView()); + receiverToSites = DataFactory.createMap(ptaScene.getView().getClasses().size()); + methodToInvokeStmt = DataFactory.createMap(); + reachMethods = DataFactory.createSet(); + calledges = DataFactory.createSet(); + } + + public void setRMQueue(ChunkedQueue rmQueue) { + this.rmQueue = rmQueue; + } + + public Collection getReachableMethods() { + return reachMethods; + } + + // initialize the receiver to sites map with the number of locals * an + // estimate for the number of contexts per methods + public Map> getReceiverToSitesMap() { + return receiverToSites; + } + + public Collection callSitesLookUp(VarNode receiver) { + return receiverToSites.getOrDefault(receiver, Collections.emptySet()); + } + + public SootMethod resolveNonSpecial(ClassType t, MethodSubSignature subSig) { + return virtualCalls.resolveNonSpecial(t, subSig); + } + + public OnFlyCallGraph getCallGraph() { + if (cicg == null) { + constructCallGraph(); + } + return ptaScene.getCallGraph(); + } + + public OnFlyCallGraph getCICallGraph() { + if (cicg == null) { + constructCallGraph(); + } + return cicg; + } + + private void constructCallGraph() { + cicg = new OnFlyCallGraph(); + Map>> map = DataFactory.createMap(); + calledges.forEach( + e -> { + ptaScene.getCallGraph().addEdge(e); + SootMethod src = e.src(); + SootMethod tgt = e.tgt(); + Stmt unit = e.srcUnit(); + Map> submap = + map.computeIfAbsent(unit, k -> DataFactory.createMap()); + Set set = submap.computeIfAbsent(src, k -> DataFactory.createSet()); + if (set.add(tgt)) { + cicg.addEdge( + new Edge( + new ContextMethod(src, pta.emptyContext()), + e.srcUnit(), + new ContextMethod(tgt, pta.emptyContext()), + e.kind())); + } + }); + } + + public List getEntryPoints() { + Node thisRef = pag.getMethodPAG(ptaScene.getFakeMainMethod()).nodeFactory().caseThis(); + thisRef = pta.parameterize(thisRef, pta.emptyContext()); + pag.addEdge(pta.getRootNode(), thisRef); + return Collections.singletonList( + pta.parameterize(ptaScene.getFakeMainMethod(), pta.emptyContext())); + } + + public void initReachableMethods() { + for (ContextMethod momc : getEntryPoints()) { + if (reachMethods.add(momc)) { + rmQueue.add(momc); + } + } + } + + public VarNode getReceiverVarNode(Local receiver, ContextMethod m) { + if (receiver.getType() == UnknownType.getInstance()) { + System.out.println("why unknown??" + m.method() + ";;" + receiver); + throw new RuntimeException(); + } + LocalVarNode base = pag.makeLocalVarNode(receiver, receiver.getType(), m.method()); + return (VarNode) pta.parameterize(base, m.context()); + } + + protected void dispatch(AllocNode receiverNode, VirtualCallSite site) { + Type type = receiverNode.getType(); + final QueueReader targets = dispatch(type, site); + while (targets.hasNext()) { + SootMethod target = targets.next(); + if (site.iie() instanceof JSpecialInvokeExpr) { + Type calleeDeclType = target.getDeclaringClassType(); + if (!PTAUtils.canStoreType(pta.getView(), type, calleeDeclType)) { + continue; + } + } + addVirtualEdge(site.container(), site.getUnit(), target, site.kind(), receiverNode); + } + } + + private void addVirtualEdge( + ContextMethod caller, Stmt callStmt, SootMethod callee, Kind kind, AllocNode receiverNode) { + Context tgtContext = pta.createCalleeCtx(caller, receiverNode, new CallSite(callStmt), callee); + ContextMethod cstarget = pta.parameterize(callee, tgtContext); + handleCallEdge(new Edge(caller, callStmt, cstarget, kind)); + Node thisRef = pag.getMethodPAG(callee).nodeFactory().caseThis(); + thisRef = pta.parameterize(thisRef, cstarget.context()); + pag.addEdge(receiverNode, thisRef); + } + + public void injectCallEdge(Object heapOrType, ContextMethod callee, Kind kind) { + Map stmtMap = + methodToInvokeStmt.computeIfAbsent(callee.method(), k -> DataFactory.createMap()); + if (!stmtMap.containsKey(heapOrType)) { + AbstractInvokeExpr ie = + new JStaticInvokeExpr(callee.method().getSignature(), Collections.emptyList()); + JInvokeStmt stmt = new JInvokeStmt(ie, StmtPositionInfo.getNoStmtPositionInfo()); + stmtMap.put(heapOrType, stmt); + handleCallEdge( + new Edge( + pta.parameterize(ptaScene.getFakeMainMethod(), pta.emptyContext()), + stmtMap.get(heapOrType), + callee, + kind)); + } + } + + public void addStaticEdge(ContextMethod caller, Stmt callStmt, SootMethod calleem, Kind kind) { + Context typeContext = pta.createCalleeCtx(caller, null, new CallSite(callStmt), calleem); + ContextMethod callee = pta.parameterize(calleem, typeContext); + handleCallEdge(new Edge(caller, callStmt, callee, kind)); + } + + protected void handleCallEdge(Edge edge) { + if (calledges.add(edge)) { + ContextMethod callee = edge.getTgt(); + if (reachMethods.add(callee)) { + rmQueue.add(callee); + } + processCallAssign(edge); + } + } + + public boolean recordVirtualCallSite(VarNode receiver, VirtualCallSite site) { + Collection sites = + receiverToSites.computeIfAbsent(receiver, k -> DataFactory.createSet()); + return sites.add(site); + } + + public void virtualCallDispatch(PointsToSetInternal p2set, VirtualCallSite site) { + p2set.forall( + new P2SetVisitor(pta) { + public void visit(Node n) { + dispatch((AllocNode) n, site); + } + }); + } + + /** + * Adds method target as a possible target of the invoke expression in s. If target is null, only + * creates the nodes for the call site, without actually connecting them to any target method. + */ + private void processCallAssign(Edge e) { + MethodPAG srcmpag = pag.getMethodPAG(e.src()); + MethodPAG tgtmpag = pag.getMethodPAG(e.tgt()); + Stmt s = e.srcUnit(); + Context srcContext = e.srcCtxt(); + Context tgtContext = e.tgtCtxt(); + MethodNodeFactory srcnf = srcmpag.nodeFactory(); + MethodNodeFactory tgtnf = tgtmpag.nodeFactory(); + SootMethod tgtmtd = tgtmpag.getMethod(); + AbstractInvokeExpr ie = s.getInvokeExpr(); + // add arg --> param edges. + int numArgs = ie.getArgCount(); + for (int i = 0; i < numArgs; i++) { + Value arg = ie.getArg(i); + if (!(arg.getType() instanceof ReferenceType) || arg instanceof NullConstant) { + continue; + } + Type tgtType = tgtmtd.getParameterType(i); + if (!(tgtType instanceof ReferenceType)) { + continue; + } + Node argNode = srcnf.getNode(arg); + argNode = pta.parameterize(argNode, srcContext); + Node parm = tgtnf.caseParm(i); + parm = pta.parameterize(parm, tgtContext); + pag.addEdge(argNode, parm); + } + // add normal return edge + if (s instanceof JAssignStmt) { + Value dest = ((JAssignStmt) s).getLeftOp(); + + if (dest.getType() instanceof ReferenceType) { + Node destNode = srcnf.getNode(dest); + destNode = pta.parameterize(destNode, srcContext); + if (tgtmtd.getReturnType() instanceof ReferenceType) { + Node retNode = tgtnf.caseRet(); + retNode = pta.parameterize(retNode, tgtContext); + pag.addEdge(retNode, destNode); + } + } + } + // add throw return edge + if (CoreConfig.v().getPtaConfig().preciseExceptions) { + Node throwNode = tgtnf.caseMethodThrow(); + /* + * If an invocation statement may throw exceptions, we create a special local variables + * to receive the exception objects. + * a_ret = x.foo(); here, a_ret is a variable to receive values from return variables of foo(); + * a_throw = x.foo(); here, a_throw is a variable to receive exception values thrown by foo(); + * */ + throwNode = pta.parameterize(throwNode, tgtContext); + MethodNodeFactory mnf = srcmpag.nodeFactory(); + Node dst = mnf.makeInvokeStmtThrowVarNode(s, srcmpag.getMethod()); + dst = pta.parameterize(dst, srcContext); + pag.addEdge(throwNode, dst); + } + } + + public QueueReader dispatch(Type type, VirtualCallSite site) { + final ChunkedQueue targetsQueue = new ChunkedQueue<>(); + final QueueReader targets = targetsQueue.reader(); + if (site.kind() == Kind.THREAD + && !PTAUtils.canStoreType(ptaScene.getView(), type, clRunnable)) { + return targets; + } + ContextMethod container = site.container(); + if (site.iie() instanceof JSpecialInvokeExpr && site.kind() != Kind.THREAD) { + SootMethod target = + virtualCalls.resolveSpecial( + (JSpecialInvokeExpr) site.iie(), site.subSig(), container.method()); + // if the call target resides in a phantom class then + // "target" will be null, simply do not add the target in that case + if (target != null) { + targetsQueue.add(target); + } + } else { + Type mType = site.recNode().getType(); + virtualCalls.resolve(type, mType, site.subSig(), container.method(), targetsQueue); + } + return targets; + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/builder/ExceptionHandler.java b/sootup.qilin/src/main/java/qilin/core/builder/ExceptionHandler.java new file mode 100644 index 00000000000..986efabc42f --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/builder/ExceptionHandler.java @@ -0,0 +1,105 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.builder; + +import java.util.*; +import qilin.core.PTA; +import qilin.core.context.Context; +import qilin.core.pag.*; +import qilin.core.sets.P2SetVisitor; +import qilin.core.sets.PointsToSetInternal; +import qilin.util.DataFactory; +import qilin.util.PTAUtils; +import sootup.core.jimple.basic.Trap; +import sootup.core.jimple.common.stmt.JIdentityStmt; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.SootMethod; +import sootup.core.types.Type; + +public class ExceptionHandler { + protected final Map> throwNodeToSites; + protected PTA pta; + protected PAG pag; + + public ExceptionHandler(PTA pta) { + this.pta = pta; + this.pag = pta.getPag(); + this.throwNodeToSites = DataFactory.createMap(pta.getView().getClasses().size()); + } + + public Collection throwSitesLookUp(VarNode throwNode) { + return throwNodeToSites.getOrDefault(throwNode, Collections.emptySet()); + } + + public boolean addThrowSite(Node throwNode, ExceptionThrowSite ets) { + Collection throwSites = + throwNodeToSites.computeIfAbsent(throwNode, k -> DataFactory.createSet()); + return throwSites.add(ets); + } + + public void exceptionDispatch(PointsToSetInternal p2set, ExceptionThrowSite site) { + p2set.forall( + new P2SetVisitor(pta) { + public void visit(Node n) { + dispatch((AllocNode) n, site); + } + }); + } + + /* + * dispatch the exception objects by following the exception-cath-links in Doop-ISSTA09. + * */ + public void dispatch(AllocNode throwObj, ExceptionThrowSite site) { + Type type = throwObj.getType(); + ContextMethod momc = site.container(); + SootMethod sm = momc.method(); + Context context = momc.context(); + MethodPAG mpag = pag.getMethodPAG(sm); + MethodNodeFactory nodeFactory = mpag.nodeFactory(); + VarNode throwNode = site.getThrowNode(); + List trapList = + mpag.stmt2wrapperedTraps.getOrDefault(site.getUnit(), Collections.emptyList()); + for (Trap trap : trapList) { + if (PTAUtils.canStoreType(pta.getView(), type, trap.getExceptionType())) { + Stmt handler = trap.getHandlerStmt(); + assert handler instanceof JIdentityStmt; + JIdentityStmt handlerStmt = (JIdentityStmt) handler; + Node caughtParam = nodeFactory.getNode(handlerStmt.getRightOp()); + Node dst = pta.parameterize(caughtParam, context); + pag.addEdge(throwObj, dst); + // record an edge from base --> caughtParam on the methodPag. + recordImplictEdge(throwNode, caughtParam, mpag); + return; + } + } + // No trap handle the throwable object in the method. + Node methodThrowNode = nodeFactory.caseMethodThrow(); + Node dst = pta.parameterize(methodThrowNode, context); + pag.addEdge(throwObj, dst); + // record an edge from base --> methodThrowNode on the methodPag. + recordImplictEdge(throwNode, methodThrowNode, mpag); + } + + private void recordImplictEdge(Node src, Node dst, MethodPAG mpag) { + if (src instanceof ContextVarNode) { + src = ((ContextVarNode) src).base(); + } + mpag.addExceptionEdge(src, dst); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/builder/FakeMainFactory.java b/sootup.qilin/src/main/java/qilin/core/builder/FakeMainFactory.java new file mode 100644 index 00000000000..faa7846ca95 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/builder/FakeMainFactory.java @@ -0,0 +1,427 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.builder; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Optional; +import qilin.CoreConfig; +import qilin.core.ArtificialMethod; +import qilin.util.PTAUtils; +import sootup.core.IdentifierFactory; +import sootup.core.frontend.OverridingBodySource; +import sootup.core.frontend.OverridingClassSource; +import sootup.core.graph.MutableStmtGraph; +import sootup.core.inputlocation.EagerInputLocation; +import sootup.core.jimple.Jimple; +import sootup.core.jimple.basic.Immediate; +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.basic.NoPositionInformation; +import sootup.core.jimple.basic.StmtPositionInfo; +import sootup.core.jimple.basic.Value; +import sootup.core.jimple.common.ref.JStaticFieldRef; +import sootup.core.jimple.common.stmt.FallsThroughStmt; +import sootup.core.jimple.common.stmt.JNopStmt; +import sootup.core.jimple.common.stmt.JReturnVoidStmt; +import sootup.core.model.*; +import sootup.core.signatures.FieldSignature; +import sootup.core.signatures.MethodSignature; +import sootup.core.signatures.MethodSubSignature; +import sootup.core.types.ClassType; +import sootup.core.views.View; +import sootup.java.core.JavaIdentifierFactory; + +public class FakeMainFactory extends ArtificialMethod { + public static FakeMainFactory instance; + + public static int implicitCallEdges; + private final SootClass fakeClass; + private final SootClass mainClass; + private final EntryPoints entryPoints; + + public FakeMainFactory(View view, SootClass mainClazz) { + super(view); + this.mainClass = mainClazz; + this.entryPoints = new EntryPoints(); + this.localStart = 0; + String className = "qilin.pta.FakeMain"; + IdentifierFactory fact = view.getIdentifierFactory(); + ClassType declaringClassSignature = JavaIdentifierFactory.getInstance().getClassType(className); + FieldSignature ctSig = + fact.getFieldSignature("currentThread", declaringClassSignature, "java.lang.Thread"); + SootField currentThread = + new SootField(ctSig, EnumSet.of(FieldModifier.STATIC), NoPositionInformation.getInstance()); + FieldSignature gtSig = + fact.getFieldSignature("globalThrow", declaringClassSignature, "java.lang.Exception"); + SootField globalThrow = + new SootField(gtSig, EnumSet.of(FieldModifier.STATIC), NoPositionInformation.getInstance()); + + MethodSignature methodSignatureOne = + view.getIdentifierFactory() + .getMethodSignature(className, "main", "void", Collections.emptyList()); + + StmtPositionInfo noPosInfo = StmtPositionInfo.getNoStmtPositionInfo(); + final JReturnVoidStmt returnVoidStmt = new JReturnVoidStmt(noPosInfo); + final JNopStmt jNop = new JNopStmt(noPosInfo); + this.bodyBuilder = Body.builder(); + makeFakeMain(currentThread); + final MutableStmtGraph stmtGraph = bodyBuilder.getStmtGraph(); + stmtGraph.addBlock(stmtList); + stmtGraph.setStartingStmt(jNop); + stmtGraph.putEdge(jNop, stmtList.get(0)); + stmtGraph.putEdge((FallsThroughStmt) stmtList.get(stmtList.size() - 1), returnVoidStmt); + + bodyBuilder + .setMethodSignature(methodSignatureOne) + .setPosition(NoPositionInformation.getInstance()); + + Body bodyOne = bodyBuilder.build(); + SootMethod dummyMainMethod = + new SootMethod( + new OverridingBodySource(methodSignatureOne, bodyOne), + methodSignatureOne, + EnumSet.of(MethodModifier.PUBLIC, MethodModifier.STATIC), + Collections.emptyList(), + NoPositionInformation.getInstance()); + this.method = dummyMainMethod; + this.fakeClass = + new SootClass( + new OverridingClassSource( + Collections.singleton(dummyMainMethod), + new LinkedHashSet<>(Arrays.asList(currentThread, globalThrow)), + EnumSet.of(ClassModifier.PUBLIC), + null, + JavaIdentifierFactory.getInstance().getClassType("java.lang.Object"), + null, + NoPositionInformation.getInstance(), + null, + view.getIdentifierFactory().getClassType(className), + new EagerInputLocation()), + SourceType.Application); + } + + public SootMethod getFakeMain() { + return this.method; + } + + private List getEntryPoints() { + List ret = new ArrayList<>(); + if (CoreConfig.v().getPtaConfig().clinitMode == CoreConfig.ClinitMode.FULL) { + ret.addAll(entryPoints.clinits()); + } else { + // on the fly mode, resolve the clinit methods on the fly. + ret.addAll(Collections.emptySet()); + } + + if (CoreConfig.v().getPtaConfig().singleentry) { + List entries = entryPoints.application(); + if (entries.isEmpty()) { + throw new RuntimeException("Must specify MAINCLASS when appmode enabled!!!"); + } else { + ret.addAll(entries); + } + } else { + ret.addAll(entryPoints.application()); + ret.addAll(entryPoints.implicit()); + } + System.out.println("#EntrySize:" + ret.size()); + return ret; + } + + public JStaticFieldRef getFieldCurrentThread() { + SootField field = fakeClass.getField("currentThread").get(); + return Jimple.newStaticFieldRef(field.getSignature()); + } + + public Value getFieldGlobalThrow() { + SootField field = fakeClass.getField("globalThrow").get(); + return Jimple.newStaticFieldRef(field.getSignature()); + } + + private void makeFakeMain(SootField currentThread) { + implicitCallEdges = 0; + for (SootMethod entry : getEntryPoints()) { + if (entry.isStatic()) { + if (entry + .getSignature() + .getSubSignature() + .toString() + .equals("void main(java.lang.String[])")) { + Value mockStr = getNew(PTAUtils.getClassType("java.lang.String")); + Immediate strArray = getNewArray(PTAUtils.getClassType("java.lang.String")); + addAssign(getArrayRef(strArray), mockStr); + addInvoke(entry.getSignature().toString(), strArray); + implicitCallEdges++; + } else if (CoreConfig.v().getPtaConfig().clinitMode != CoreConfig.ClinitMode.ONFLY + || !PTAUtils.isStaticInitializer(entry)) { + // in the on fly mode, we won't add a call directly for methods. + addInvoke(entry.getSignature().toString()); + implicitCallEdges++; + } + } + } + if (CoreConfig.v().getPtaConfig().singleentry) { + return; + } + Local sv = getNextLocal(PTAUtils.getClassType("java.lang.String")); + Local mainThread = getNew(PTAUtils.getClassType("java.lang.Thread")); + Local mainThreadGroup = getNew(PTAUtils.getClassType("java.lang.ThreadGroup")); + Local systemThreadGroup = getNew(PTAUtils.getClassType("java.lang.ThreadGroup")); + + JStaticFieldRef gCurrentThread = Jimple.newStaticFieldRef(currentThread.getSignature()); + addAssign(gCurrentThread, mainThread); // Store + Local vRunnable = getNextLocal(PTAUtils.getClassType("java.lang.Runnable")); + + Local lThreadGroup = getNextLocal(PTAUtils.getClassType("java.lang.ThreadGroup")); + addInvoke( + mainThread, + "(java.lang.ThreadGroup,java.lang.String)>", + mainThreadGroup, + sv); + Local tmpThread = getNew(PTAUtils.getClassType("java.lang.Thread")); + addInvoke( + tmpThread, + "(java.lang.ThreadGroup,java.lang.Runnable)>", + lThreadGroup, + vRunnable); + addInvoke(tmpThread, ""); + + addInvoke(systemThreadGroup, "()>"); + addInvoke( + mainThreadGroup, + "(java.lang.ThreadGroup,java.lang.String)>", + systemThreadGroup, + sv); + + Local lThread = getNextLocal(PTAUtils.getClassType("java.lang.Thread")); + Local lThrowable = getNextLocal(PTAUtils.getClassType("java.lang.Throwable")); + Local tmpThreadGroup = getNew(PTAUtils.getClassType("java.lang.ThreadGroup")); + addInvoke( + tmpThreadGroup, + "", + lThread, + lThrowable); // TODO. + + // ClassLoader + Local defaultClassLoader = getNew(PTAUtils.getClassType("sun.misc.Launcher$AppClassLoader")); + addInvoke(defaultClassLoader, "()>"); + Local vClass = getNextLocal(PTAUtils.getClassType("java.lang.Class")); + Local vDomain = getNextLocal(PTAUtils.getClassType("java.security.ProtectionDomain")); + addInvoke( + defaultClassLoader, + "", + sv); + addInvoke( + defaultClassLoader, + "", + vClass, + vDomain); + addInvoke( + defaultClassLoader, "", vClass); + + // PrivilegedActionException + Local privilegedActionException = + getNew(PTAUtils.getClassType("java.security.PrivilegedActionException")); + Local gLthrow = getNextLocal(PTAUtils.getClassType("java.lang.Exception")); + addInvoke( + privilegedActionException, + "(java.lang.Exception)>", + gLthrow); + } + + public class EntryPoints { + + final MethodSubSignature sigMain; + final MethodSubSignature sigFinalize; + final MethodSubSignature sigExit; + final MethodSubSignature sigClinit; + final MethodSubSignature sigInit; + final MethodSubSignature sigStart; + final MethodSubSignature sigRun; + final MethodSubSignature sigObjRun; + final MethodSubSignature sigForName; + + private EntryPoints() { + sigMain = JavaIdentifierFactory.getInstance().parseMethodSubSignature(JavaMethods.SIG_MAIN); + sigFinalize = + JavaIdentifierFactory.getInstance().parseMethodSubSignature(JavaMethods.SIG_FINALIZE); + + sigExit = JavaIdentifierFactory.getInstance().parseMethodSubSignature(JavaMethods.SIG_EXIT); + sigClinit = + JavaIdentifierFactory.getInstance().parseMethodSubSignature(JavaMethods.SIG_CLINIT); + sigInit = JavaIdentifierFactory.getInstance().parseMethodSubSignature(JavaMethods.SIG_INIT); + sigStart = JavaIdentifierFactory.getInstance().parseMethodSubSignature(JavaMethods.SIG_START); + sigRun = JavaIdentifierFactory.getInstance().parseMethodSubSignature(JavaMethods.SIG_RUN); + sigObjRun = + JavaIdentifierFactory.getInstance().parseMethodSubSignature(JavaMethods.SIG_OBJ_RUN); + sigForName = + JavaIdentifierFactory.getInstance().parseMethodSubSignature(JavaMethods.SIG_FOR_NAME); + } + + protected void addMethod(List set, SootClass cls, MethodSubSignature methodSubSig) { + Optional osm = cls.getMethod(methodSubSig); + osm.ifPresent(set::add); + } + + protected void addMethod(List set, String methodSig) { + MethodSignature ms = JavaIdentifierFactory.getInstance().parseMethodSignature(methodSig); + Optional osm = view.getMethod(ms); + osm.ifPresent(set::add); + } + + /** + * Returns only the application entry points, not including entry points invoked implicitly by + * the VM. + */ + public List application() { + List ret = new ArrayList<>(); + if (mainClass != null) { + addMethod(ret, mainClass, sigMain); + for (SootMethod clinit : clinitsOf(mainClass)) { + ret.add(clinit); + } + } + return ret; + } + + /** Returns only the entry points invoked implicitly by the VM. */ + public List implicit() { + List ret = new ArrayList(); + + // if (Options.v().src_prec() == Options.src_prec_dotnet) { + // return ret; + // } + + addMethod(ret, JavaMethods.INITIALIZE_SYSTEM_CLASS); + addMethod(ret, JavaMethods.THREAD_GROUP_INIT); + // addMethod( ret, ""); + addMethod(ret, JavaMethods.THREAD_EXIT); + addMethod(ret, JavaMethods.THREADGROUP_UNCAUGHT_EXCEPTION); + // addMethod( ret, ""); + addMethod(ret, JavaMethods.CLASSLOADER_INIT); + addMethod(ret, JavaMethods.CLASSLOADER_LOAD_CLASS_INTERNAL); + addMethod(ret, JavaMethods.CLASSLOADER_CHECK_PACKAGE_ACC); + addMethod(ret, JavaMethods.CLASSLOADER_ADD_CLASS); + addMethod(ret, JavaMethods.CLASSLOADER_FIND_NATIVE); + addMethod(ret, JavaMethods.PRIV_ACTION_EXC_INIT); + // addMethod( ret, ""); + addMethod(ret, JavaMethods.RUN_FINALIZE); + addMethod(ret, JavaMethods.THREAD_INIT_RUNNABLE); + addMethod(ret, JavaMethods.THREAD_INIT_STRING); + return ret; + } + + /** Returns all the entry points. */ + public List all() { + List ret = new ArrayList(); + ret.addAll(application()); + ret.addAll(implicit()); + return ret; + } + + /** Returns a list of all static initializers. */ + public List clinits() { + List ret = new ArrayList<>(); + Collection classes = view.getClasses(); + for (SootClass cl : classes) { + addMethod(ret, cl, sigClinit); + } + return ret; + } + + /** Returns a list of all clinits of class cl and its superclasses. */ + public Iterable clinitsOf(SootClass cl) { + // Do not create an actual list, since this method gets called quite often + // Instead, callers usually just want to iterate over the result. + Optional oinit = cl.getMethod(sigClinit); + Optional osuperClass = cl.getSuperclass(); + // check super classes until finds a constructor or no super class there anymore. + while (!oinit.isPresent() && osuperClass.isPresent()) { + ClassType superType = osuperClass.get(); + Optional oSuperClass = view.getClass(superType); + if (!oSuperClass.isPresent()) { + break; + } + SootClass superClass = oSuperClass.get(); + oinit = superClass.getMethod(sigClinit); + osuperClass = superClass.getSuperclass(); + } + if (!oinit.isPresent()) { + return Collections.emptyList(); + } + SootMethod initStart = oinit.get(); + return () -> + new Iterator() { + SootMethod current = initStart; + + @Override + public SootMethod next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + SootMethod n = current; + + // Pre-fetch the next element + current = null; + Optional oCurrentClass = + view.getClass(n.getDeclaringClassType()); + if (!oCurrentClass.isPresent()) { + return n; + } + SootClass currentClass = oCurrentClass.get(); + while (true) { + Optional osuperType1 = currentClass.getSuperclass(); + if (!osuperType1.isPresent()) { + break; + } + ClassType classType = osuperType1.get(); + Optional osuperClass1 = view.getClass(classType); + if (!osuperClass1.isPresent()) { + break; + } + SootClass superClass = osuperClass1.get(); + Optional om = superClass.getMethod(sigClinit); + if (om.isPresent()) { + current = om.get(); + break; + } + currentClass = superClass; + } + + return n; + } + + @Override + public boolean hasNext() { + return current != null; + } + }; + } + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/builder/JavaMethods.java b/sootup.qilin/src/main/java/qilin/core/builder/JavaMethods.java new file mode 100644 index 00000000000..1ae37825497 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/builder/JavaMethods.java @@ -0,0 +1,59 @@ +package qilin.core.builder; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 2022 Fraunhofer SIT + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +public class JavaMethods { + + public static final String SIG_MAIN = "void main(java.lang.String[])"; + public static final String SIG_FINALIZE = "void finalize()"; + public static final String SIG_EXIT = "void exit()"; + public static final String SIG_CLINIT = "void ()"; + public static final String SIG_INIT = "void ()"; + public static final String SIG_START = "void start()"; + public static final String SIG_RUN = "void run()"; + public static final String SIG_OBJ_RUN = "java.lang.Object run()"; + public static final String SIG_FOR_NAME = "java.lang.Class forName(java.lang.String)"; + + public static final String INITIALIZE_SYSTEM_CLASS = + ""; + public static final String THREAD_GROUP_INIT = "()>"; + public static final String THREAD_EXIT = ""; + public static final String THREADGROUP_UNCAUGHT_EXCEPTION = + ""; + public static final String CLASSLOADER_INIT = "()>"; + public static final String CLASSLOADER_LOAD_CLASS_INTERNAL = + ""; + public static final String CLASSLOADER_CHECK_PACKAGE_ACC = + ""; + public static final String CLASSLOADER_ADD_CLASS = + ""; + public static final String CLASSLOADER_FIND_NATIVE = + ""; + public static final String PRIV_ACTION_EXC_INIT = + "(java.lang.Exception)>"; + public static final String RUN_FINALIZE = ""; + public static final String THREAD_INIT_RUNNABLE = + "(java.lang.ThreadGroup,java.lang.Runnable)>"; + public static final String THREAD_INIT_STRING = + "(java.lang.ThreadGroup,java.lang.String)>"; +} diff --git a/sootup.qilin/src/main/java/qilin/core/builder/MethodNodeFactory.java b/sootup.qilin/src/main/java/qilin/core/builder/MethodNodeFactory.java new file mode 100644 index 00000000000..42acdc1918c --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/builder/MethodNodeFactory.java @@ -0,0 +1,428 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.builder; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Optional; +import java.util.Queue; +import java.util.Set; +import javax.annotation.Nonnull; +import qilin.CoreConfig; +import qilin.core.PTAScene; +import qilin.core.PointsToAnalysis; +import qilin.core.pag.*; +import qilin.core.pag.Field; +import qilin.util.PTAUtils; +import qilin.util.queue.UniqueQueue; +import sootup.core.jimple.basic.Immediate; +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.basic.NoPositionInformation; +import sootup.core.jimple.basic.Value; +import sootup.core.jimple.common.constant.ClassConstant; +import sootup.core.jimple.common.constant.IntConstant; +import sootup.core.jimple.common.constant.NullConstant; +import sootup.core.jimple.common.constant.StringConstant; +import sootup.core.jimple.common.expr.AbstractInstanceInvokeExpr; +import sootup.core.jimple.common.expr.AbstractInvokeExpr; +import sootup.core.jimple.common.expr.JCastExpr; +import sootup.core.jimple.common.expr.JNewArrayExpr; +import sootup.core.jimple.common.expr.JNewExpr; +import sootup.core.jimple.common.expr.JNewMultiArrayExpr; +import sootup.core.jimple.common.ref.JArrayRef; +import sootup.core.jimple.common.ref.JCaughtExceptionRef; +import sootup.core.jimple.common.ref.JInstanceFieldRef; +import sootup.core.jimple.common.ref.JParameterRef; +import sootup.core.jimple.common.ref.JStaticFieldRef; +import sootup.core.jimple.common.ref.JThisRef; +import sootup.core.jimple.common.stmt.JAssignStmt; +import sootup.core.jimple.common.stmt.JIdentityStmt; +import sootup.core.jimple.common.stmt.JReturnStmt; +import sootup.core.jimple.common.stmt.JThrowStmt; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.jimple.javabytecode.stmt.JExitMonitorStmt; +import sootup.core.jimple.visitor.AbstractStmtVisitor; +import sootup.core.model.*; +import sootup.core.signatures.FieldSignature; +import sootup.core.signatures.MethodSubSignature; +import sootup.core.types.ArrayType; +import sootup.core.types.ClassType; +import sootup.core.types.ReferenceType; +import sootup.core.types.Type; +import sootup.java.core.JavaIdentifierFactory; +import sootup.java.core.language.JavaJimple; + +/** @author Ondrej Lhotak */ +public class MethodNodeFactory { + protected PAG pag; + protected MethodPAG mpag; + protected SootMethod method; + private final PTAScene scene; + + public MethodNodeFactory(PAG pag, MethodPAG mpag) { + this.pag = pag; + this.mpag = mpag; + method = mpag.getMethod(); + this.scene = pag.getPta().getScene(); + } + + public Node getNode(Value v) { + if (v instanceof Local) { + Local l = (Local) v; + return caseLocal(l); + } else if (v instanceof JCastExpr) { + JCastExpr castExpr = (JCastExpr) v; + return caseCastExpr(castExpr); + } else if (v instanceof JNewExpr) { + JNewExpr ne = (JNewExpr) v; + return caseNewExpr(ne); + } else if (v instanceof JStaticFieldRef) { + JStaticFieldRef sfr = (JStaticFieldRef) v; + return caseStaticFieldRef(sfr); + } else if (v instanceof JNewArrayExpr) { + JNewArrayExpr nae = (JNewArrayExpr) v; + return caseNewArrayExpr(nae); + } else if (v instanceof JArrayRef) { + JArrayRef ar = (JArrayRef) v; + return caseArrayRef(ar); + } else if (v instanceof ClassConstant) { + ClassConstant cc = (ClassConstant) v; + return caseClassConstant(cc); + } else if (v instanceof StringConstant) { + StringConstant sc = (StringConstant) v; + return caseStringConstant(sc); + } else if (v instanceof JCaughtExceptionRef) { + JCaughtExceptionRef cef = (JCaughtExceptionRef) v; + return caseCaughtExceptionRef(cef); + } else if (v instanceof JParameterRef) { + JParameterRef pr = (JParameterRef) v; + return caseParameterRef(pr); + } else if (v instanceof NullConstant) { + NullConstant nc = (NullConstant) v; + return caseNullConstant(nc); + } else if (v instanceof JInstanceFieldRef) { + JInstanceFieldRef ifr = (JInstanceFieldRef) v; + return caseInstanceFieldRef(ifr); + } else if (v instanceof JThisRef) { + return caseThis(); + } else if (v instanceof JNewMultiArrayExpr) { + JNewMultiArrayExpr nmae = (JNewMultiArrayExpr) v; + return caseNewMultiArrayExpr(nmae); + } + System.out.println(v + ";;" + v.getClass()); + return null; + } + + /** Adds the edges required for this statement to the graph. */ + public final void handleStmt(Stmt s) { + if (s.containsInvokeExpr()) { + mpag.addCallStmt(s); + handleInvokeStmt(s); + } else { + handleIntraStmt(s); + } + } + + /** + * Adds the edges required for this statement to the graph. Add throw stmt if the invoke method + * throws an Exception. + */ + protected void handleInvokeStmt(Stmt s) { + AbstractInvokeExpr ie = s.getInvokeExpr(); + int numArgs = ie.getArgCount(); + for (int i = 0; i < numArgs; i++) { + Value arg = ie.getArg(i); + if (!(arg.getType() instanceof ReferenceType) || arg instanceof NullConstant) { + continue; + } + getNode(arg); + } + if (s instanceof JAssignStmt) { + JAssignStmt assignStmt = (JAssignStmt) s; + Value l = assignStmt.getLeftOp(); + if ((l.getType() instanceof ReferenceType)) { + getNode(l); + } + } + if (ie instanceof AbstractInstanceInvokeExpr) { + AbstractInstanceInvokeExpr aie = (AbstractInstanceInvokeExpr) ie; + getNode(aie.getBase()); + } + } + + private void resolveClinit(JStaticFieldRef staticFieldRef) { + FieldSignature fieldSig = staticFieldRef.getFieldSignature(); + ClassType classType = fieldSig.getDeclClassType(); + if (PTAUtils.isFakeMainClass(classType)) { // skip FakeMain + return; + } + SootClass sootClass = scene.getView().getClass(classType).get(); + clinitsOf(sootClass).forEach(mpag::addTriggeredClinit); + } + + /** Adds the edges required for this statement to the graph. */ + private void handleIntraStmt(Stmt s) { + s.accept( + new AbstractStmtVisitor() { + @Override + public void caseAssignStmt(@Nonnull JAssignStmt stmt) { + Value l = stmt.getLeftOp(); + Value r = stmt.getRightOp(); + if (l instanceof JStaticFieldRef) { + resolveClinit((JStaticFieldRef) l); + } else if (r instanceof JStaticFieldRef) { + resolveClinit((JStaticFieldRef) r); + } + + if (!(l.getType() instanceof ReferenceType)) return; + // check for improper casts, with mal-formed code we might get + // l = (refliketype)int_type, if so just return + if (r instanceof JCastExpr + && (!(((JCastExpr) r).getOp().getType() instanceof ReferenceType))) { + return; + } + + if (!(r.getType() instanceof ReferenceType)) return; + Node dest = getNode(l); + Node src = getNode(r); + mpag.addInternalEdge(src, dest); + } + + @Override + public void caseIdentityStmt(@Nonnull JIdentityStmt stmt) { + if (!(stmt.getLeftOp().getType() instanceof ReferenceType)) { + return; + } + Node dest = getNode(stmt.getLeftOp()); + Node src = getNode(stmt.getRightOp()); + mpag.addInternalEdge(src, dest); + } + + @Override + public void caseExitMonitorStmt(@Nonnull JExitMonitorStmt stmt) { + defaultCaseStmt(stmt); + } + + @Override + public void caseReturnStmt(@Nonnull JReturnStmt stmt) { + if (!(stmt.getOp().getType() instanceof ReferenceType)) return; + Node retNode = getNode(stmt.getOp()); + mpag.addInternalEdge(retNode, caseRet()); + } + + @Override + public void caseThrowStmt(@Nonnull JThrowStmt stmt) { + if (!CoreConfig.v().getPtaConfig().preciseExceptions) { + mpag.addInternalEdge(getNode(stmt.getOp()), getNode(scene.getFieldGlobalThrow())); + } + } + }); + } + + private VarNode caseLocal(Local l) { + return pag.makeLocalVarNode(l, l.getType(), method); + } + + private AllocNode caseNewArrayExpr(JNewArrayExpr nae) { + return pag.makeAllocNode(nae, nae.getType(), method); + } + + private AllocNode caseNewExpr(JNewExpr ne) { + SootClass cl = scene.getSootClass(ne.getType().toString()); + clinitsOf(cl).forEach(mpag::addTriggeredClinit); + return pag.makeAllocNode(ne, ne.getType(), method); + } + + private FieldRefNode caseInstanceFieldRef(JInstanceFieldRef ifr) { + FieldSignature fieldSig = ifr.getFieldSignature(); + Optional osf = scene.getView().getField(fieldSig); + SootField sf; + if (!osf.isPresent()) { + sf = + new SootField( + fieldSig, + Collections.singleton(FieldModifier.PUBLIC), + NoPositionInformation.getInstance()); + // System.out.println("Warnning:" + ifr + " is resolved to be a null field in Scene."); + } else { + sf = osf.get(); + } + Local base = ifr.getBase(); + return pag.makeFieldRefNode(pag.makeLocalVarNode(base, base.getType(), method), new Field(sf)); + } + + private VarNode caseNewMultiArrayExpr(JNewMultiArrayExpr nmae) { + ArrayType type = (ArrayType) nmae.getType(); + int pos = 0; + AllocNode prevAn = + pag.makeAllocNode( + JavaJimple.getInstance().newNewArrayExpr(type, nmae.getSize(pos)), type, method); + VarNode prevVn = pag.makeLocalVarNode(prevAn.getNewExpr(), prevAn.getType(), method); + mpag.addInternalEdge(prevAn, prevVn); // new + VarNode ret = prevVn; + while (true) { + Type t = type.getElementType(); + if (!(t instanceof ArrayType)) { + break; + } + type = (ArrayType) t; + ++pos; + Immediate sizeVal; + if (pos < nmae.getSizeCount()) { + sizeVal = nmae.getSize(pos); + } else { + sizeVal = IntConstant.getInstance(1); + } + AllocNode an = + pag.makeAllocNode(JavaJimple.getInstance().newNewArrayExpr(type, sizeVal), type, method); + VarNode vn = pag.makeLocalVarNode(an.getNewExpr(), an.getType(), method); + mpag.addInternalEdge(an, vn); // new + mpag.addInternalEdge(vn, pag.makeFieldRefNode(prevVn, ArrayElement.v())); // store + prevVn = vn; + } + return ret; + } + + private VarNode caseCastExpr(JCastExpr ce) { + Node opNode = getNode(ce.getOp()); + VarNode castNode = pag.makeLocalVarNode(ce, ce.getType(), method); + mpag.addInternalEdge(opNode, castNode); + return castNode; + } + + public VarNode caseThis() { + Type type = + method.isStatic() + ? PTAUtils.getClassType("java.lang.Object") + : method.getDeclaringClassType(); + VarNode ret = pag.makeLocalVarNode(new Parm(method, PointsToAnalysis.THIS_NODE), type, method); + ret.setInterProcTarget(); + return ret; + } + + public VarNode caseParm(int index) { + VarNode ret = + pag.makeLocalVarNode(new Parm(method, index), method.getParameterType(index), method); + ret.setInterProcTarget(); + return ret; + } + + public VarNode caseRet() { + VarNode ret = + pag.makeLocalVarNode( + new Parm(method, PointsToAnalysis.RETURN_NODE), method.getReturnType(), method); + ret.setInterProcSource(); + return ret; + } + + public VarNode caseMethodThrow() { + VarNode ret = + pag.makeLocalVarNode( + new Parm(method, PointsToAnalysis.THROW_NODE), + PTAUtils.getClassType("java.lang.Throwable"), + method); + ret.setInterProcSource(); + return ret; + } + + public final FieldRefNode caseArray(VarNode base) { + return pag.makeFieldRefNode(base, ArrayElement.v()); + } + + private Node caseCaughtExceptionRef(JCaughtExceptionRef cer) { + if (CoreConfig.v().getPtaConfig().preciseExceptions) { + // we model caughtException expression as an local assignment. + return pag.makeLocalVarNode(cer, cer.getType(), method); + } else { + return getNode(scene.getFieldGlobalThrow()); + } + } + + private FieldRefNode caseArrayRef(JArrayRef ar) { + return caseArray(caseLocal(ar.getBase())); + } + + private VarNode caseParameterRef(JParameterRef pr) { + return caseParm(pr.getIndex()); + } + + private VarNode caseStaticFieldRef(JStaticFieldRef sfr) { + return pag.makeGlobalVarNode(sfr.getFieldSignature(), sfr.getType()); + } + + private Node caseNullConstant(NullConstant nr) { + return null; + } + + private VarNode caseStringConstant(StringConstant sc) { + AllocNode stringConstantNode = pag.makeStringConstantNode(sc); + VarNode stringConstantVar = + pag.makeGlobalVarNode(sc, PTAUtils.getClassType("java.lang.String")); + mpag.addInternalEdge(stringConstantNode, stringConstantVar); + VarNode vn = pag.makeLocalVarNode(sc, PTAUtils.getClassType("java.lang.String"), method); + mpag.addInternalEdge(stringConstantVar, vn); + return vn; + } + + public LocalVarNode makeInvokeStmtThrowVarNode(Stmt invoke, SootMethod method) { + return pag.makeLocalVarNode(invoke, PTAUtils.getClassType("java.lang.Throwable"), method); + } + + public final VarNode caseClassConstant(ClassConstant cc) { + AllocNode classConstant = pag.makeClassConstantNode(cc); + VarNode classConstantVar = pag.makeGlobalVarNode(cc, PTAUtils.getClassType("java.lang.Class")); + mpag.addInternalEdge(classConstant, classConstantVar); + VarNode vn = pag.makeLocalVarNode(cc, PTAUtils.getClassType("java.lang.Class"), method); + mpag.addInternalEdge(classConstantVar, vn); + return vn; + } + + /* + * We use this method to replace EntryPoints.v().clinitsOf() because it is infested with bugs. + * */ + public Set clinitsOf(SootClass cl) { + Set ret = new HashSet<>(); + Set visit = new HashSet<>(); + Queue worklist = new UniqueQueue<>(); + Optional curr = Optional.of(cl.getType()); + while (curr.isPresent()) { + ClassType ct = curr.get(); + SootClass sc = scene.getView().getClass(ct).get(); + worklist.add(sc); + curr = sc.getSuperclass(); + } + while (!worklist.isEmpty()) { + SootClass sc = worklist.poll(); + if (visit.add(sc)) { + Set itfs = sc.getInterfaces(); + for (ClassType itf : itfs) { + Optional xsc = scene.getView().getClass(itf); + xsc.ifPresent(worklist::add); + } + } + } + for (SootClass sc : visit) { + MethodSubSignature subclinit = + JavaIdentifierFactory.getInstance().parseMethodSubSignature("void ()"); + final Optional initStart = sc.getMethod(subclinit); + initStart.ifPresent(ret::add); + } + return ret; + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/builder/callgraph/Edge.java b/sootup.qilin/src/main/java/qilin/core/builder/callgraph/Edge.java new file mode 100644 index 00000000000..063527bfdcf --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/builder/callgraph/Edge.java @@ -0,0 +1,269 @@ +package qilin.core.builder.callgraph; + +import qilin.core.context.Context; +import qilin.core.pag.ContextMethod; +import qilin.util.Invalidable; +import sootup.core.jimple.common.expr.AbstractInvokeExpr; +import sootup.core.jimple.common.expr.JInterfaceInvokeExpr; +import sootup.core.jimple.common.expr.JSpecialInvokeExpr; +import sootup.core.jimple.common.expr.JStaticInvokeExpr; +import sootup.core.jimple.common.expr.JVirtualInvokeExpr; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.SootMethod; + +/** + * Represents a single edge in a call graph. + * + * @author Ondrej Lhotak + */ +public final class Edge implements Invalidable { + + /** + * The method in which the call occurs; may be null for calls not occurring in a specific method + * (eg. implicit calls by the VM) + */ + private ContextMethod src; + + /** The target method of the call edge. */ + private ContextMethod tgt; + + /** + * The unit at which the call occurs; may be null for calls not occurring at a specific statement + * (eg. calls in native code) + */ + private Stmt srcUnit; + + /** + * The kind of edge. Note: kind should not be tested by other classes; instead, accessors such as + * isExplicit() should be added. + */ + private final Kind kind; + + private boolean invalid = false; + + public Edge(ContextMethod src, Stmt srcUnit, ContextMethod tgt, Kind kind) { + this.src = src; + this.srcUnit = srcUnit; + this.tgt = tgt; + this.kind = kind; + } + + public Edge(ContextMethod src, Stmt srcUnit, ContextMethod tgt) { + this.kind = ieToKind(srcUnit.getInvokeExpr()); + this.src = src; + this.srcUnit = srcUnit; + this.tgt = tgt; + } + + public SootMethod src() { + return (src == null) ? null : src.method(); + } + + public Context srcCtxt() { + return (src == null) ? null : src.context(); + } + + public ContextMethod getSrc() { + return src; + } + + public Stmt srcUnit() { + return srcUnit; + } + + public Stmt srcStmt() { + return srcUnit; + } + + public SootMethod tgt() { + return (tgt == null) ? null : tgt.method(); + } + + public Context tgtCtxt() { + return (tgt == null) ? null : tgt.context(); + } + + public ContextMethod getTgt() { + return tgt; + } + + public Kind kind() { + return kind; + } + + public static Kind ieToKind(AbstractInvokeExpr ie) { + if (ie instanceof JVirtualInvokeExpr) { + return Kind.VIRTUAL; + } else if (ie instanceof JSpecialInvokeExpr) { + return Kind.SPECIAL; + } else if (ie instanceof JInterfaceInvokeExpr) { + return Kind.INTERFACE; + } else if (ie instanceof JStaticInvokeExpr) { + return Kind.STATIC; + } else { + throw new RuntimeException(); + } + } + + /** Returns true if the call is due to an explicit invoke statement. */ + public boolean isExplicit() { + return Kind.isExplicit(this.kind); + } + + /** Returns true if the call is due to an explicit instance invoke statement. */ + public boolean isInstance() { + return Kind.isInstance(this.kind); + } + + public boolean isVirtual() { + return Kind.isVirtual(this.kind); + } + + public boolean isSpecial() { + return Kind.isSpecial(this.kind); + } + + /** Returns true if the call is to static initializer. */ + public boolean isClinit() { + return Kind.isClinit(this.kind); + } + + /** Returns true if the call is due to an explicit static invoke statement. */ + public boolean isStatic() { + return Kind.isStatic(this.kind); + } + + public boolean isThreadRunCall() { + return Kind.isThread(this.kind); + } + + public boolean passesParameters() { + return Kind.passesParameters(this.kind); + } + + @Override + public boolean isInvalid() { + return invalid; + } + + @Override + public void invalidate() { + // Since the edge remains in the QueueReaders for a while, the GC could not claim old units. + src = null; + srcUnit = null; + tgt = null; + invalid = true; + } + + @Override + public int hashCode() { + if (invalid) { + return 0; + } + int ret = (tgt.hashCode() + 20) + (kind == null ? 0 : kind.getNumber()); + if (src != null) { + ret = ret * 32 + src.hashCode(); + } + if (srcUnit != null) { + ret = ret * 32 + srcUnit.hashCode(); + } + return ret; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof Edge)) { + return false; + } + Edge o = (Edge) other; + return (o.src == this.src) && (o.srcUnit == srcUnit) && (o.tgt == tgt) && (o.kind == kind); + } + + @Override + public String toString() { + return this.kind + " edge: " + srcUnit + " in " + src + " ==> " + tgt; + } + + private Edge nextByUnit = this; + private Edge prevByUnit = this; + private Edge nextBySrc = this; + private Edge prevBySrc = this; + private Edge nextByTgt = this; + private Edge prevByTgt = this; + + void insertAfterByUnit(Edge other) { + nextByUnit = other.nextByUnit; + nextByUnit.prevByUnit = this; + other.nextByUnit = this; + prevByUnit = other; + } + + void insertAfterBySrc(Edge other) { + nextBySrc = other.nextBySrc; + nextBySrc.prevBySrc = this; + other.nextBySrc = this; + prevBySrc = other; + } + + void insertAfterByTgt(Edge other) { + nextByTgt = other.nextByTgt; + nextByTgt.prevByTgt = this; + other.nextByTgt = this; + prevByTgt = other; + } + + void insertBeforeByUnit(Edge other) { + prevByUnit = other.prevByUnit; + prevByUnit.nextByUnit = this; + other.prevByUnit = this; + nextByUnit = other; + } + + void insertBeforeBySrc(Edge other) { + prevBySrc = other.prevBySrc; + prevBySrc.nextBySrc = this; + other.prevBySrc = this; + nextBySrc = other; + } + + void insertBeforeByTgt(Edge other) { + prevByTgt = other.prevByTgt; + prevByTgt.nextByTgt = this; + other.prevByTgt = this; + nextByTgt = other; + } + + void remove() { + invalid = true; + nextByUnit.prevByUnit = prevByUnit; + prevByUnit.nextByUnit = nextByUnit; + nextBySrc.prevBySrc = prevBySrc; + prevBySrc.nextBySrc = nextBySrc; + nextByTgt.prevByTgt = prevByTgt; + prevByTgt.nextByTgt = nextByTgt; + } + + Edge nextByUnit() { + return nextByUnit; + } + + Edge nextBySrc() { + return nextBySrc; + } + + Edge nextByTgt() { + return nextByTgt; + } + + Edge prevByUnit() { + return prevByUnit; + } + + Edge prevBySrc() { + return prevBySrc; + } + + Edge prevByTgt() { + return prevByTgt; + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/builder/callgraph/Kind.java b/sootup.qilin/src/main/java/qilin/core/builder/callgraph/Kind.java new file mode 100644 index 00000000000..408d34e230e --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/builder/callgraph/Kind.java @@ -0,0 +1,239 @@ +package qilin.core.builder.callgraph; + +import qilin.util.Numberable; + +/** + * Enumeration type representing the kind of a call graph edge. + * + * @author Ondrej Lhotak + */ +public final class Kind implements Numberable { + + public static final Kind INVALID = new Kind("INVALID"); + + /** Due to explicit invokestatic instruction. */ + public static final Kind STATIC = new Kind("STATIC"); + + /** Due to explicit invokevirtual instruction. */ + public static final Kind VIRTUAL = new Kind("VIRTUAL"); + + /** Due to explicit invokeinterface instruction. */ + public static final Kind INTERFACE = new Kind("INTERFACE"); + + /** Due to explicit invokespecial instruction. */ + public static final Kind SPECIAL = new Kind("SPECIAL"); + + /** Implicit call to static initializer. */ + public static final Kind CLINIT = new Kind("CLINIT"); + + /** Fake edges from our generic callback model. */ + public static final Kind GENERIC_FAKE = new Kind("GENERIC_FAKE"); + + /** Implicit call to Thread.run() due to Thread.start() call. */ + public static final Kind THREAD = new Kind("THREAD"); + + /** Implicit call to java.lang.Runnable.run() due to Executor.execute() call. */ + public static final Kind EXECUTOR = new Kind("EXECUTOR"); + + /** Implicit call to AsyncTask.doInBackground() due to AsyncTask.execute() call. */ + public static final Kind ASYNCTASK = new Kind("ASYNCTASK"); + + /** Implicit call to java.lang.ref.Finalizer.register from new bytecode. */ + public static final Kind FINALIZE = new Kind("FINALIZE"); + + /** + * Implicit call to Handler.handleMessage(android.os.Message) due to Handler.sendxxxxMessagexxxx() + * call. + */ + public static final Kind HANDLER = new Kind("HANDLER"); + + /** Implicit call to finalize() from java.lang.ref.Finalizer.invokeFinalizeMethod(). */ + public static final Kind INVOKE_FINALIZE = new Kind("INVOKE_FINALIZE"); + + /** Implicit call to run() through AccessController.doPrivileged(). */ + public static final Kind PRIVILEGED = new Kind("PRIVILEGED"); + + /** Implicit call to constructor from java.lang.Class.newInstance(). */ + public static final Kind NEWINSTANCE = new Kind("NEWINSTANCE"); + + /** Due to call to Method.invoke(..). */ + public static final Kind REFL_INVOKE = new Kind("REFL_METHOD_INVOKE"); + + /** Due to call to Constructor.newInstance(..). */ + public static final Kind REFL_CONSTR_NEWINSTANCE = new Kind("REFL_CONSTRUCTOR_NEWINSTANCE"); + + /** Due to call to Class.newInstance(..) when reflection log is enabled. */ + public static final Kind REFL_CLASS_NEWINSTANCE = new Kind("REFL_CLASS_NEWINSTANCE"); + + private final String name; + private int num; + + private Kind(String name) { + this.name = name; + } + + public String name() { + return name; + } + + @Override + public int getNumber() { + return num; + } + + @Override + public void setNumber(int num) { + this.num = num; + } + + @Override + public String toString() { + return name(); + } + + public boolean passesParameters() { + return passesParameters(this); + } + + public boolean isFake() { + return isFake(this); + } + + /** Returns true if the call is due to an explicit invoke statement. */ + public boolean isExplicit() { + return isExplicit(this); + } + + /** Returns true if the call is due to an explicit instance invoke statement. */ + public boolean isInstance() { + return isInstance(this); + } + + /** Returns true if the call is due to an explicit virtual invoke statement. */ + public boolean isVirtual() { + return isVirtual(this); + } + + public boolean isSpecial() { + return isSpecial(this); + } + + /** Returns true if the call is to static initializer. */ + public boolean isClinit() { + return isClinit(this); + } + + /** Returns true if the call is due to an explicit static invoke statement. */ + public boolean isStatic() { + return isStatic(this); + } + + public boolean isThread() { + return isThread(this); + } + + public boolean isExecutor() { + return isExecutor(this); + } + + public boolean isAsyncTask() { + return isAsyncTask(this); + } + + public boolean isPrivileged() { + return isPrivileged(this); + } + + public boolean isReflection() { + return isReflection(this); + } + + public boolean isReflInvoke() { + return isReflInvoke(this); + } + + public boolean isHandler() { + return isHandler(this); + } + + public static boolean passesParameters(Kind k) { + return isExplicit(k) + || k == THREAD + || k == EXECUTOR + || k == ASYNCTASK + || k == FINALIZE + || k == PRIVILEGED + || k == NEWINSTANCE + || k == INVOKE_FINALIZE + || k == REFL_INVOKE + || k == REFL_CONSTR_NEWINSTANCE + || k == REFL_CLASS_NEWINSTANCE + || k == GENERIC_FAKE; + } + + public static boolean isFake(Kind k) { + return k == THREAD + || k == EXECUTOR + || k == ASYNCTASK + || k == PRIVILEGED + || k == HANDLER + || k == GENERIC_FAKE; + } + + /** Returns true if the call is due to an explicit invoke statement. */ + public static boolean isExplicit(Kind k) { + return isInstance(k) || isStatic(k); + } + + /** Returns true if the call is due to an explicit instance invoke statement. */ + public static boolean isInstance(Kind k) { + return k == VIRTUAL || k == INTERFACE || k == SPECIAL; + } + + /** Returns true if the call is due to an explicit virtual invoke statement. */ + public static boolean isVirtual(Kind k) { + return k == VIRTUAL; + } + + public static boolean isSpecial(Kind k) { + return k == SPECIAL; + } + + /** Returns true if the call is to static initializer. */ + public static boolean isClinit(Kind k) { + return k == CLINIT; + } + + /** Returns true if the call is due to an explicit static invoke statement. */ + public static boolean isStatic(Kind k) { + return k == STATIC; + } + + public static boolean isThread(Kind k) { + return k == THREAD; + } + + public static boolean isExecutor(Kind k) { + return k == EXECUTOR; + } + + public static boolean isAsyncTask(Kind k) { + return k == ASYNCTASK; + } + + public static boolean isPrivileged(Kind k) { + return k == PRIVILEGED; + } + + public static boolean isReflection(Kind k) { + return k == REFL_CLASS_NEWINSTANCE || k == REFL_CONSTR_NEWINSTANCE || k == REFL_INVOKE; + } + + public static boolean isReflInvoke(Kind k) { + return k == REFL_INVOKE; + } + + public static boolean isHandler(Kind k) { + return k == HANDLER; + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/builder/callgraph/OnFlyCallGraph.java b/sootup.qilin/src/main/java/qilin/core/builder/callgraph/OnFlyCallGraph.java new file mode 100644 index 00000000000..d7b8caa1fb3 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/builder/callgraph/OnFlyCallGraph.java @@ -0,0 +1,490 @@ +package qilin.core.builder.callgraph; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 2003 Ondrej Lhotak + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; +import javax.annotation.Nonnull; +import qilin.core.pag.ContextMethod; +import qilin.util.DataFactory; +import qilin.util.queue.ChunkedQueue; +import qilin.util.queue.QueueReader; +import sootup.callgraph.MutableCallGraph; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.SootMethod; +import sootup.core.signatures.MethodSignature; + +/** + * Represents the edges in a call graph. This class is meant to act as only a container of edges; + * code for various call graph builders should be kept out of it, as well as most code for accessing + * the edges. + * + * @author Ondrej Lhotak + */ +public class OnFlyCallGraph implements MutableCallGraph, Iterable { + protected Set methods = DataFactory.createSet(); + protected Map> calls = DataFactory.createMap(); + protected int callCnt = 0; + + protected Set edges = new LinkedHashSet<>(); + protected ChunkedQueue stream = new ChunkedQueue<>(); + protected QueueReader reader = stream.reader(); + protected Map srcMethodToEdge = new LinkedHashMap<>(); + protected Map srcUnitToEdge = new LinkedHashMap<>(); + protected Map tgtToEdge = new LinkedHashMap<>(); + protected Edge dummy = new Edge(null, null, null, Kind.INVALID); + + /** Used to add an edge to the call graph. Returns true iff the edge was not already present. */ + public boolean addEdge(Edge e) { + if (!edges.add(e)) { + return false; + } + MethodSignature srcSig = e.getSrc().method().getSignature(); + MethodSignature tgtSig = e.getTgt().method().getSignature(); + addMethod(srcSig); + addMethod(tgtSig); + addCall(srcSig, tgtSig); + stream.add(e); + + Edge position = srcUnitToEdge.get(e.srcUnit()); + if (position == null) { + srcUnitToEdge.put(e.srcUnit(), e); + position = dummy; + } + e.insertAfterByUnit(position); + + position = srcMethodToEdge.get(e.getSrc()); + if (position == null) { + srcMethodToEdge.put(e.getSrc(), e); + position = dummy; + } + e.insertAfterBySrc(position); + + position = tgtToEdge.get(e.getTgt()); + if (position == null) { + tgtToEdge.put(e.getTgt(), e); + position = dummy; + } + e.insertAfterByTgt(position); + return true; + } + + /** + * Removes all outgoing edges that start at the given unit + * + * @param u The unit from which to remove all outgoing edges + * @return True if at least one edge has been removed, otherwise false + */ + public boolean removeAllEdgesOutOf(Stmt u) { + boolean hasRemoved = false; + Set edgesToRemove = new HashSet<>(); + for (QueueReader edgeRdr = listener(); edgeRdr.hasNext(); ) { + Edge e = edgeRdr.next(); + if (e.srcUnit() == u) { + e.remove(); + removeEdge(e, false); + edgesToRemove.add(e); + hasRemoved = true; + } + } + if (hasRemoved) { + reader.remove(edgesToRemove); + } + return hasRemoved; + } + + /** + * Swaps an invocation statement. All edges that previously went from the given statement to some + * callee now go from the new statement to the same callee. This method is intended to be used + * when a Jimple statement is replaced, but the replacement does not semantically affect the + * edges. + * + * @param out The old statement + * @param in The new statement + * @return True if at least one edge was affected by this operation + */ + public boolean swapEdgesOutOf(Stmt out, Stmt in) { + boolean hasSwapped = false; + for (Iterator edgeRdr = edgesOutOf(out); edgeRdr.hasNext(); ) { + Edge e = edgeRdr.next(); + ContextMethod src = e.getSrc(); + ContextMethod tgt = e.getTgt(); + removeEdge(e); + e.remove(); + addEdge(new Edge(src, in, tgt)); + hasSwapped = true; + } + return hasSwapped; + } + + /** + * Removes the edge e from the call graph. Returns true iff the edge was originally present in the + * call graph. + */ + public boolean removeEdge(Edge e) { + return removeEdge(e, true); + } + + /** + * Removes the edge e from the call graph. Returns true iff the edge was originally present in the + * call graph. + * + * @param e the edge + * @param removeInEdgeList when true (recommended), it is ensured that the edge reader is informed + * about the removal + * @return whether the removal was successful. + */ + public boolean removeEdge(Edge e, boolean removeInEdgeList) { + if (!edges.remove(e)) { + return false; + } + MethodSignature srcSig = e.getSrc().method().getSignature(); + MethodSignature tgtSig = e.getTgt().method().getSignature(); + Set tgtSigs = calls.getOrDefault(srcSig, Collections.emptySet()); + assert (!tgtSigs.isEmpty()); + tgtSigs.remove(tgtSig); + // !FIXME only edge is removed. I do not remove the added nodes. + e.remove(); + + if (srcUnitToEdge.get(e.srcUnit()) == e) { + if (e.nextByUnit().srcUnit() == e.srcUnit()) { + srcUnitToEdge.put(e.srcUnit(), e.nextByUnit()); + } else { + srcUnitToEdge.remove(e.srcUnit()); + } + } + + if (srcMethodToEdge.get(e.getSrc()) == e) { + if (e.nextBySrc().getSrc() == e.getSrc()) { + srcMethodToEdge.put(e.getSrc(), e.nextBySrc()); + } else { + srcMethodToEdge.remove(e.getSrc()); + } + } + + if (tgtToEdge.get(e.getTgt()) == e) { + if (e.nextByTgt().getTgt() == e.getTgt()) { + tgtToEdge.put(e.getTgt(), e.nextByTgt()); + } else { + tgtToEdge.remove(e.getTgt()); + } + } + // This is an linear operation, so we want to avoid it if possible. + if (removeInEdgeList) { + reader.remove(e); + } + return true; + } + + /** + * Removes the edges from the call graph. Returns true iff one edge was originally present in the + * call graph. + * + * @param edges the edges + * @return whether the removal was successful. + */ + public boolean removeEdges(Collection edges) { + if (!this.edges.removeAll(edges)) { + return false; + } + for (Edge e : edges) { + removeEdge(e, false); + } + reader.remove(edges); + return true; + } + + /** + * Does this method have no incoming edge? + * + * @param method + * @return + */ + public boolean isEntryMethod(SootMethod method) { + return !tgtToEdge.containsKey(method); + } + + /** + * Find the specific call edge that is going out from the callsite u and the call target is + * callee. Without advanced data structure, we can only sequentially search for the match. + * Fortunately, the number of outgoing edges for a unit is not too large. + * + * @param u + * @param callee + * @return + */ + public Edge findEdge(Stmt u, SootMethod callee) { + Edge e = srcUnitToEdge.get(u); + if (e != null) { + while (e.srcUnit() == u && e.kind() != Kind.INVALID) { + if (e.tgt() == callee) { + return e; + } + e = e.nextByUnit(); + } + } + return null; + } + + /** Returns an iterator over all methods that are the sources of at least one edge. */ + public Iterator sourceMethods() { + return srcMethodToEdge.keySet().iterator(); + } + + /** Returns an iterator over all edges that have u as their source unit. */ + public Iterator edgesOutOf(Stmt u) { + return new TargetsOfUnitIterator(u); + } + + class TargetsOfUnitIterator implements Iterator { + private final Stmt u; + private Edge position; + + TargetsOfUnitIterator(Stmt u) { + this.u = u; + if (u == null) { + throw new RuntimeException(); + } + this.position = srcUnitToEdge.get(u); + if (position == null) { + position = dummy; + } + } + + @Override + public boolean hasNext() { + if (position.srcUnit() != u) { + return false; + } + return position.kind() != Kind.INVALID; + } + + @Override + public Edge next() { + Edge ret = position; + position = position.nextByUnit(); + return ret; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + + /** Returns an iterator over all edges that have m as their source method. */ + public Iterator edgesOutOf(ContextMethod m) { + return new TargetsOfMethodIterator(m); + } + + class TargetsOfMethodIterator implements Iterator { + private final ContextMethod m; + private Edge position; + + TargetsOfMethodIterator(ContextMethod m) { + this.m = m; + if (m == null) { + throw new RuntimeException(); + } + this.position = srcMethodToEdge.get(m); + if (position == null) { + position = dummy; + } + } + + @Override + public boolean hasNext() { + if (position == dummy) { + return false; + } + if (!position.getSrc().equals(m)) { + return false; + } + return position.kind() != Kind.INVALID; + } + + @Override + public Edge next() { + Edge ret = position; + position = position.nextBySrc(); + return ret; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + + /** Returns an iterator over all edges that have m as their target method. */ + public Iterator edgesInto(ContextMethod m) { + return new CallersOfMethodIterator(m); + } + + class CallersOfMethodIterator implements Iterator { + private final ContextMethod m; + private Edge position; + + CallersOfMethodIterator(ContextMethod m) { + this.m = m; + if (m == null) { + throw new RuntimeException(); + } + this.position = tgtToEdge.get(m); + if (position == null) { + position = dummy; + } + } + + @Override + public boolean hasNext() { + if (position == dummy) { + return false; + } + if (!position.getTgt().equals(m)) { + return false; + } + return position.kind() != Kind.INVALID; + } + + @Override + public Edge next() { + Edge ret = position; + position = position.nextByTgt(); + return ret; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + + /** + * Returns a QueueReader object containing all edges added so far, and which will be informed of + * any new edges that are later added to the graph. + */ + public QueueReader listener() { + return reader.clone(); + } + + /** + * Returns a QueueReader object which will contain ONLY NEW edges which will be added to the + * graph. + */ + public QueueReader newListener() { + return stream.reader(); + } + + @Override + public String toString() { + StringBuilder out = new StringBuilder(); + for (QueueReader reader = listener(); reader.hasNext(); ) { + Edge e = reader.next(); + out.append(e.toString()).append('\n'); + } + return out.toString(); + } + + /** Returns the number of edges in the call graph. */ + public int size() { + return edges.size(); + } + + @Override + public Iterator iterator() { + return edges.iterator(); + } + + /* implements APIs from MutableCallGraph*/ + @Override + public void addMethod(@Nonnull MethodSignature calledMethod) { + this.methods.add(calledMethod); + } + + @Override + public void addCall( + @Nonnull MethodSignature sourceMethod, @Nonnull MethodSignature targetMethod) { + Set targets = + this.calls.computeIfAbsent(sourceMethod, k -> DataFactory.createSet()); + if (targets.add(targetMethod)) { + ++callCnt; + } + } + + @Nonnull + @Override + public Set getMethodSignatures() { + return new HashSet<>(this.methods); + } + + @Nonnull + @Override + public MutableCallGraph copy() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean containsMethod(@Nonnull MethodSignature method) { + return this.methods.contains(method); + } + + @Override + public boolean containsCall( + @Nonnull MethodSignature sourceMethod, @Nonnull MethodSignature targetMethod) { + if (this.calls.containsKey(sourceMethod)) { + if (this.calls.get(sourceMethod).contains(targetMethod)) { + return true; + } + } + return false; + } + + @Override + public int callCount() { + return callCnt; + } + + @Override + public String exportAsDot() { + throw new UnsupportedOperationException(); + } + + @Nonnull + @Override + public Set callsFrom(@Nonnull MethodSignature sourceMethod) { + return this.calls.getOrDefault(sourceMethod, Collections.emptySet()); + } + + @Nonnull + @Override + public Set callsTo(@Nonnull MethodSignature targetMethod) { + throw new UnsupportedOperationException(); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/context/Context.java b/sootup.qilin/src/main/java/qilin/core/context/Context.java new file mode 100644 index 00000000000..e0cc97af768 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/context/Context.java @@ -0,0 +1,29 @@ +package qilin.core.context; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 2003 Ondrej Lhotak + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +/** + * A context in a context-sensitive all graph. May be a unit (in a 1CFA call graph) or a Spark + * AllocNode (in an object-sensitive call graph). + */ +public interface Context {} diff --git a/sootup.qilin/src/main/java/qilin/core/context/ContextElement.java b/sootup.qilin/src/main/java/qilin/core/context/ContextElement.java new file mode 100644 index 00000000000..5bec7e90783 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/context/ContextElement.java @@ -0,0 +1,27 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.context; + +public interface ContextElement { + @Override + int hashCode(); + + @Override + boolean equals(Object obj); +} diff --git a/sootup.qilin/src/main/java/qilin/core/context/ContextElements.java b/sootup.qilin/src/main/java/qilin/core/context/ContextElements.java new file mode 100644 index 00000000000..a6a38d59d57 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/context/ContextElements.java @@ -0,0 +1,119 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.context; + +import java.util.Arrays; + +public class ContextElements implements Context { + private final ContextElement[] array; + private final int size; + private int hashCode = 0; + + public ContextElements(ContextElement[] array, int s) { + this.array = array; + this.size = s; + } + + public ContextElement[] getElements() { + return array; + } + + public ContextElement get(int i) { + return array[i]; + } + + public boolean contains(ContextElement heap) { + for (int i = 0; i < size; ++i) { + if (array[i] == heap) { + return true; + } + } + return false; + } + + public boolean isEmpty() { + return size == 0; + } + + public int size() { + return size; + } + + @Override + public int hashCode() { + if (hashCode == 0) { + hashCode = Arrays.hashCode(array); + } + return hashCode; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (getClass() != obj.getClass()) return false; + ContextElements other = (ContextElements) obj; + + // if (!Arrays.equals(array, other.array)) return false; + + // custom array equals for context + // allows for checking of different sized arrays, but represent same + // context-sensitive heap object + if (this.array == null || other.array == null) return false; + + if (this.size() != other.size()) return false; + + for (int i = 0; i < size(); i++) { + Object o1 = this.array[i]; + Object o2 = other.array[i]; + if (!(o1 == null ? o2 == null : o1.equals(o2))) return false; + } + + return true; + } + + @Override + public String toString() { + StringBuilder localStringBuilder = new StringBuilder(); + localStringBuilder.append('['); + for (int i = 0; i < array.length; i++) { + localStringBuilder.append(array[i]); + if (i < array.length - 1) { + localStringBuilder.append(", "); + } + } + localStringBuilder.append(']'); + return localStringBuilder.toString(); + } + + /** + * Compose a new context by a given context and a heap. The depth of new context is also required. + */ + public static ContextElements newContext(ContextElements c, ContextElement ce, int depth) { + ContextElement[] array; + if (c.size() < depth) { + array = new ContextElement[c.size() + 1]; + System.arraycopy(c.getElements(), 0, array, 1, c.size()); + } else { + array = new ContextElement[depth]; + System.arraycopy(c.getElements(), 0, array, 1, depth - 1); + } + array[0] = ce; + return new ContextElements(array, array.length); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/context/TypeContextElement.java b/sootup.qilin/src/main/java/qilin/core/context/TypeContextElement.java new file mode 100644 index 00000000000..5886a20056a --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/context/TypeContextElement.java @@ -0,0 +1,78 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.context; + +import qilin.core.pag.AllocNode; +import qilin.core.pag.ClassConstantNode; +import qilin.core.pag.StringConstantNode; +import qilin.util.PTAUtils; +import sootup.core.model.SootMethod; +import sootup.core.types.Type; + +/** Type based context element in the points to analysis. */ +public class TypeContextElement implements ContextElement { + + private final Type type; + + private TypeContextElement(Type type) { + this.type = type; + } + + public static TypeContextElement getTypeContextElement(AllocNode a) { + SootMethod declaringMethod = a.getMethod(); + Type declType = PTAUtils.getClassType("java.lang.Object"); + if (declaringMethod != null) { + declType = declaringMethod.getDeclaringClassType(); + } else if (a instanceof ClassConstantNode) { + declType = PTAUtils.getClassType("java.lang.System"); + } else if (a instanceof StringConstantNode) { + declType = PTAUtils.getClassType("java.lang.String"); + } + return new TypeContextElement(declType); + } + + public Type getType() { + return type; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((type == null) ? 0 : type.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + TypeContextElement other = (TypeContextElement) obj; + if (type == null) { + return other.type == null; + } else { + return type.equals(other.type); + } + } + + public String toString() { + return "TypeContext: " + type; + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/natives/JavaIoFileSystemGetFileSystemNative.java b/sootup.qilin/src/main/java/qilin/core/natives/JavaIoFileSystemGetFileSystemNative.java new file mode 100644 index 00000000000..366483949bc --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/natives/JavaIoFileSystemGetFileSystemNative.java @@ -0,0 +1,43 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.natives; + +import qilin.util.PTAUtils; +import sootup.core.jimple.basic.Local; +import sootup.core.model.SootMethod; +import sootup.core.views.View; + +public class JavaIoFileSystemGetFileSystemNative extends NativeMethod { + public JavaIoFileSystemGetFileSystemNative(View view, SootMethod method) { + super(view, method); + } + + /** ********************** java.io.FileSystem ********************** */ + /** + * Returns a variable pointing to the file system constant + * + *

public static native java.io.FileSystem getFileSystem(); only exists in old version of + * JDK(e.g., JDK6). + */ + protected void simulateImpl() { + Local newLocal0 = getNew(PTAUtils.getClassType("java.io.UnixFileSystem")); + addInvoke(newLocal0, "()>"); + addReturn(newLocal0); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/natives/JavaIoFileSystemListNative.java b/sootup.qilin/src/main/java/qilin/core/natives/JavaIoFileSystemListNative.java new file mode 100644 index 00000000000..9e797e5a39c --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/natives/JavaIoFileSystemListNative.java @@ -0,0 +1,41 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.natives; + +import qilin.util.PTAUtils; +import sootup.core.jimple.basic.Immediate; +import sootup.core.jimple.basic.Value; +import sootup.core.model.SootMethod; +import sootup.core.views.View; + +public class JavaIoFileSystemListNative extends NativeMethod { + public JavaIoFileSystemListNative(View view, SootMethod method) { + super(view, method); + } + + /** ********************** java.io.FileSystem ********************** */ + /** Returns a String[] only exists in old JDK(e.g., JDK6). */ + protected void simulateImpl() { + Immediate arrLocal = getNewArray(PTAUtils.getClassType("java.lang.String")); + Value elem = getNew(PTAUtils.getClassType("java.lang.String")); + // addInvoke(elem, "()>"); + addAssign(getArrayRef(arrLocal), elem); + addReturn(arrLocal); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/natives/JavaLangObjectCloneNative.java b/sootup.qilin/src/main/java/qilin/core/natives/JavaLangObjectCloneNative.java new file mode 100644 index 00000000000..9f4ad4ccd46 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/natives/JavaLangObjectCloneNative.java @@ -0,0 +1,39 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.natives; + +import sootup.core.jimple.basic.Local; +import sootup.core.model.SootMethod; +import sootup.core.views.View; + +public class JavaLangObjectCloneNative extends NativeMethod { + + public JavaLangObjectCloneNative(View view, SootMethod method) { + super(view, method); + } + + /** + * Implements the abstract method simulateMethod. It distributes the request to the corresponding + * methods by signatures. + */ + protected void simulateImpl() { + Local r0 = getThis(); + addReturn(r0); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/natives/JavaLangRefFinalizerInvokeFinalizeMethodNative.java b/sootup.qilin/src/main/java/qilin/core/natives/JavaLangRefFinalizerInvokeFinalizeMethodNative.java new file mode 100644 index 00000000000..11d65468719 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/natives/JavaLangRefFinalizerInvokeFinalizeMethodNative.java @@ -0,0 +1,40 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.natives; + +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.basic.StmtPositionInfo; +import sootup.core.jimple.common.stmt.JReturnVoidStmt; +import sootup.core.model.SootMethod; +import sootup.core.views.View; + +public class JavaLangRefFinalizerInvokeFinalizeMethodNative extends NativeMethod { + public JavaLangRefFinalizerInvokeFinalizeMethodNative(View view, SootMethod method) { + super(view, method); + } + + /** "<java.lang.ref.Finalizer: void invokeFinalizeMethod(java.lang.Object)>" */ + protected void simulateImpl() { + Local r0 = getPara(0); + addInvoke(r0, ""); + final JReturnVoidStmt returnStmt = + new JReturnVoidStmt(StmtPositionInfo.getNoStmtPositionInfo()); + stmtList.add(returnStmt); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/natives/JavaLangReflectArrayGet.java b/sootup.qilin/src/main/java/qilin/core/natives/JavaLangReflectArrayGet.java new file mode 100644 index 00000000000..de5cfbea239 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/natives/JavaLangReflectArrayGet.java @@ -0,0 +1,47 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.natives; + +import qilin.util.PTAUtils; +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.basic.Value; +import sootup.core.model.SootMethod; +import sootup.core.types.ArrayType; +import sootup.core.types.ClassType; +import sootup.core.views.View; + +/* + * + * */ + +public class JavaLangReflectArrayGet extends NativeMethod { + JavaLangReflectArrayGet(View view, SootMethod method) { + super(view, method); + } + + @Override + protected void simulateImpl() { + ClassType objType = PTAUtils.getClassType("java.lang.Object"); + Value arrayBase = getPara(0, new ArrayType(objType, 1)); + Value arrayRef = getArrayRef(arrayBase); + Local ret = getNextLocal(objType); + addAssign(ret, arrayRef); + addReturn(ret); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/natives/JavaLangReflectArraySet.java b/sootup.qilin/src/main/java/qilin/core/natives/JavaLangReflectArraySet.java new file mode 100644 index 00000000000..58e6331865a --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/natives/JavaLangReflectArraySet.java @@ -0,0 +1,52 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.natives; + +import qilin.util.PTAUtils; +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.basic.StmtPositionInfo; +import sootup.core.jimple.basic.Value; +import sootup.core.jimple.common.ref.JArrayRef; +import sootup.core.jimple.common.stmt.JReturnVoidStmt; +import sootup.core.model.SootMethod; +import sootup.core.types.ArrayType; +import sootup.core.types.ClassType; +import sootup.core.views.View; + +/* + * handle + * */ + +public class JavaLangReflectArraySet extends NativeMethod { + JavaLangReflectArraySet(View view, SootMethod method) { + super(view, method); + } + + @Override + protected void simulateImpl() { + ClassType objType = PTAUtils.getClassType("java.lang.Object"); + Local arrayBase = getPara(0, new ArrayType(objType, 1)); + Value rightValue = getPara(2); + JArrayRef arrayRef = getArrayRef(arrayBase); + addAssign(arrayRef, rightValue); // a[] = b; + final JReturnVoidStmt returnStmt = + new JReturnVoidStmt(StmtPositionInfo.getNoStmtPositionInfo()); + stmtList.add(returnStmt); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/natives/JavaLangSystemArraycopyNative.java b/sootup.qilin/src/main/java/qilin/core/natives/JavaLangSystemArraycopyNative.java new file mode 100644 index 00000000000..506ef2a5f02 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/natives/JavaLangSystemArraycopyNative.java @@ -0,0 +1,59 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.natives; + +import qilin.util.PTAUtils; +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.basic.StmtPositionInfo; +import sootup.core.jimple.basic.Value; +import sootup.core.jimple.common.ref.JArrayRef; +import sootup.core.jimple.common.stmt.JReturnVoidStmt; +import sootup.core.model.SootMethod; +import sootup.core.types.ArrayType; +import sootup.core.types.ClassType; +import sootup.core.views.View; + +/* + * This file will be temporarily discarded. Yes, it is correct but need one more level of context. + * Thus it will make qilin.spark less precise than its counterpart in Doop. + * */ +public class JavaLangSystemArraycopyNative extends NativeMethod { + public JavaLangSystemArraycopyNative(View view, SootMethod method) { + super(view, method); + } + + /** + * never make a[] = b[], it violates the principle of jimple statement. make a temporary variable. + */ + protected void simulateImpl() { + // Value srcArr = getPara(0); + // Value dstArr = getPara(2); + ClassType objType = PTAUtils.getClassType("java.lang.Object"); + Value srcArr = getPara(0, new ArrayType(objType, 1)); + Value dstArr = getPara(2, new ArrayType(objType, 1)); + Value src = getArrayRef(srcArr); + JArrayRef dst = getArrayRef(dstArr); + Local temp = getNextLocal(objType); + addAssign(temp, src); + addAssign(dst, temp); + final JReturnVoidStmt returnStmt = + new JReturnVoidStmt(StmtPositionInfo.getNoStmtPositionInfo()); + stmtList.add(returnStmt); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/natives/JavaLangSystemSetErr0Native.java b/sootup.qilin/src/main/java/qilin/core/natives/JavaLangSystemSetErr0Native.java new file mode 100644 index 00000000000..a88e92176db --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/natives/JavaLangSystemSetErr0Native.java @@ -0,0 +1,47 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.natives; + +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.basic.StmtPositionInfo; +import sootup.core.jimple.common.ref.JStaticFieldRef; +import sootup.core.jimple.common.stmt.JReturnVoidStmt; +import sootup.core.model.SootMethod; +import sootup.core.views.View; + +public class JavaLangSystemSetErr0Native extends NativeMethod { + public JavaLangSystemSetErr0Native(View view, SootMethod method) { + super(view, method); + } + + /** + * NOTE: this native method is not documented in JDK API. It should have the side effect: + * System.err = parameter + * + *

private static native void setErr0(java.io.PrintStream); + */ + protected void simulateImpl() { + Local r1 = getPara(0); + JStaticFieldRef systemErr = getStaticFieldRef("java.lang.System", "err"); + addAssign(systemErr, r1); + final JReturnVoidStmt returnStmt = + new JReturnVoidStmt(StmtPositionInfo.getNoStmtPositionInfo()); + stmtList.add(returnStmt); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/natives/JavaLangSystemSetIn0Native.java b/sootup.qilin/src/main/java/qilin/core/natives/JavaLangSystemSetIn0Native.java new file mode 100644 index 00000000000..35fae72b488 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/natives/JavaLangSystemSetIn0Native.java @@ -0,0 +1,47 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.natives; + +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.basic.StmtPositionInfo; +import sootup.core.jimple.common.ref.JStaticFieldRef; +import sootup.core.jimple.common.stmt.JReturnVoidStmt; +import sootup.core.model.SootMethod; +import sootup.core.views.View; + +public class JavaLangSystemSetIn0Native extends NativeMethod { + public JavaLangSystemSetIn0Native(View view, SootMethod method) { + super(view, method); + } + + /** + * NOTE: this native method is not documented in JDK API. It should have the side effect: + * System.in = parameter + * + *

private static native void setIn0(java.io.InputStream); + */ + protected void simulateImpl() { + Local r1 = getPara(0); + JStaticFieldRef systemIn = getStaticFieldRef("java.lang.System", "in"); + addAssign(systemIn, r1); + final JReturnVoidStmt returnStmt = + new JReturnVoidStmt(StmtPositionInfo.getNoStmtPositionInfo()); + stmtList.add(returnStmt); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/natives/JavaLangSystemSetOut0Native.java b/sootup.qilin/src/main/java/qilin/core/natives/JavaLangSystemSetOut0Native.java new file mode 100644 index 00000000000..0f35ede9b31 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/natives/JavaLangSystemSetOut0Native.java @@ -0,0 +1,47 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.natives; + +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.basic.StmtPositionInfo; +import sootup.core.jimple.common.ref.JStaticFieldRef; +import sootup.core.jimple.common.stmt.JReturnVoidStmt; +import sootup.core.model.SootMethod; +import sootup.core.views.View; + +public class JavaLangSystemSetOut0Native extends NativeMethod { + public JavaLangSystemSetOut0Native(View view, SootMethod method) { + super(view, method); + } + + /** + * NOTE: this native method is not documented in JDK API. It should have the side effect: + * System.out = parameter + * + *

private static native void setOut0(java.io.PrintStream); + */ + protected void simulateImpl() { + Local r1 = getPara(0); + JStaticFieldRef systemOut = getStaticFieldRef("java.lang.System", "out"); + addAssign(systemOut, r1); + final JReturnVoidStmt returnStmt = + new JReturnVoidStmt(StmtPositionInfo.getNoStmtPositionInfo()); + stmtList.add(returnStmt); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/natives/JavaLangThreadCurrentThread.java b/sootup.qilin/src/main/java/qilin/core/natives/JavaLangThreadCurrentThread.java new file mode 100644 index 00000000000..0d1b47e5b6e --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/natives/JavaLangThreadCurrentThread.java @@ -0,0 +1,41 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.natives; + +import qilin.util.PTAUtils; +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.basic.Value; +import sootup.core.model.SootMethod; +import sootup.core.views.View; + +public class JavaLangThreadCurrentThread extends NativeMethod { + private final Value currentThread; + + JavaLangThreadCurrentThread(View view, SootMethod method, Value currentThread) { + super(view, method); + this.currentThread = currentThread; + } + + @Override + protected void simulateImpl() { + Local lv = getNextLocal(PTAUtils.getClassType("java.lang.Thread")); + addAssign(lv, currentThread); + addReturn(lv); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/natives/JavaLangThreadStart0Native.java b/sootup.qilin/src/main/java/qilin/core/natives/JavaLangThreadStart0Native.java new file mode 100644 index 00000000000..3152e48a7a7 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/natives/JavaLangThreadStart0Native.java @@ -0,0 +1,50 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.natives; + +import sootup.core.jimple.basic.LValue; +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.basic.StmtPositionInfo; +import sootup.core.jimple.common.stmt.JReturnVoidStmt; +import sootup.core.model.SootMethod; +import sootup.core.views.View; + +public class JavaLangThreadStart0Native extends NativeMethod { + private final LValue currentThread; + + public JavaLangThreadStart0Native(View view, SootMethod method, LValue currentThread) { + super(view, method); + this.currentThread = currentThread; + } + + /** + * Calls to Thread.start() get redirected to Thread.run. + * + *

In JRE 1.5 and JRE 1.6 Thread.start() is defined in Java and there is native method start0. + */ + @Override + protected void simulateImpl() { + Local mThis = getThis(); + addInvoke(mThis, ""); + addAssign(currentThread, mThis); // store. + final JReturnVoidStmt returnStmt = + new JReturnVoidStmt(StmtPositionInfo.getNoStmtPositionInfo()); + stmtList.add(returnStmt); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/natives/JavaSecurityAccessControllerDoPrivilegedNative.java b/sootup.qilin/src/main/java/qilin/core/natives/JavaSecurityAccessControllerDoPrivilegedNative.java new file mode 100644 index 00000000000..89c223f15ac --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/natives/JavaSecurityAccessControllerDoPrivilegedNative.java @@ -0,0 +1,39 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ +package qilin.core.natives; + +import sootup.core.jimple.basic.Local; +import sootup.core.model.SootMethod; +import sootup.core.views.View; + +public class JavaSecurityAccessControllerDoPrivilegedNative extends NativeMethod { + public JavaSecurityAccessControllerDoPrivilegedNative(View view, SootMethod method) { + super(view, method); + } + + /** + * public static native java.lang.Object doPrivileged(java.security.PrivilegedAction) public + * static native java.lang.Object + * doPrivileged(java.security.PrivilegedAction,java.security.AccessControlContext) + */ + protected void simulateImpl() { + Local r0 = getPara(0); + Local r1 = getInvoke(r0, ""); + addReturn(r1); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/natives/JavaSecurityAccessControllerDoPrivileged_ExceptionNative.java b/sootup.qilin/src/main/java/qilin/core/natives/JavaSecurityAccessControllerDoPrivileged_ExceptionNative.java new file mode 100644 index 00000000000..0ccdd4727ef --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/natives/JavaSecurityAccessControllerDoPrivileged_ExceptionNative.java @@ -0,0 +1,40 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.natives; + +import sootup.core.jimple.basic.Local; +import sootup.core.model.SootMethod; +import sootup.core.views.View; + +public class JavaSecurityAccessControllerDoPrivileged_ExceptionNative extends NativeMethod { + public JavaSecurityAccessControllerDoPrivileged_ExceptionNative(View view, SootMethod method) { + super(view, method); + } + + /** + * public static native java.lang.Object doPrivileged(java.security.PrivilegedExceptionAction) + * public static native java.lang.Object + * doPrivileged(java.security.PrivilegedExceptionAction,java.security.AccessControlContext) + */ + protected void simulateImpl() { + Local r0 = getPara(0); + Local r1 = getInvoke(r0, ""); + addReturn(r1); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/natives/NativeMethod.java b/sootup.qilin/src/main/java/qilin/core/natives/NativeMethod.java new file mode 100644 index 00000000000..8bd0fb725aa --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/natives/NativeMethod.java @@ -0,0 +1,53 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.natives; + +import java.util.Collections; +import qilin.core.ArtificialMethod; +import qilin.util.PTAUtils; +import sootup.core.graph.MutableStmtGraph; +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.Body; +import sootup.core.model.SootMethod; +import sootup.core.views.View; + +public abstract class NativeMethod extends ArtificialMethod { + NativeMethod(View view, SootMethod method) { + super(view); + this.method = method; + Body body = PTAUtils.getMethodBody(method); + this.bodyBuilder = Body.builder(body, Collections.emptySet()); + int paraCount = method.getParameterCount(); + paraLocals = new Local[paraCount]; + this.paraStart = method.isStatic() ? 0 : 1; + this.localStart = this.paraStart + paraCount; + } + + protected abstract void simulateImpl(); + + public void simulate() { + simulateImpl(); + MutableStmtGraph stmtGraph = bodyBuilder.getStmtGraph(); + stmtGraph.addBlock(stmtList); + Stmt curr = stmtList.get(0); + stmtGraph.setStartingStmt(curr); + PTAUtils.updateMethodBody(method, bodyBuilder.build()); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/natives/NativeMethodDriver.java b/sootup.qilin/src/main/java/qilin/core/natives/NativeMethodDriver.java new file mode 100644 index 00000000000..a589006f132 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/natives/NativeMethodDriver.java @@ -0,0 +1,93 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.natives; + +import qilin.core.PTAScene; +import sootup.core.model.SootMethod; +import sootup.core.views.View; + +public class NativeMethodDriver { + protected final PTAScene ptaScene; + protected final View view; + + public NativeMethodDriver(PTAScene ptaScene) { + this.ptaScene = ptaScene; + this.view = ptaScene.getView(); + } + + public void buildNative(SootMethod method) { + if (!ptaScene.nativeBuilt.add(method)) { + return; + } + String sig = method.getSignature().toString(); + switch (sig) { + case "": + case "": + new JavaLangObjectCloneNative(view, method).simulate(); + break; + case "": + new JavaLangSystemSetIn0Native(view, method).simulate(); + break; + case "": + new JavaLangSystemSetOut0Native(view, method).simulate(); + break; + case "": + new JavaLangSystemSetErr0Native(view, method).simulate(); + break; + // case "": + // new JavaLangSystemArraycopyNative(method).simulate(); + // break; + case "": + case "": + new JavaIoFileSystemGetFileSystemNative(view, method).simulate(); + break; + case "": + case "": + new JavaIoFileSystemListNative(view, method).simulate(); + break; + case "": + new JavaLangRefFinalizerInvokeFinalizeMethodNative(view, method).simulate(); + break; + case "": + case "": + new JavaSecurityAccessControllerDoPrivilegedNative(view, method).simulate(); + break; + case "": + case "": + new JavaSecurityAccessControllerDoPrivileged_ExceptionNative(view, method).simulate(); + break; + case "": + new JavaLangThreadCurrentThread(view, method, ptaScene.getFieldCurrentThread()).simulate(); + break; + case "": + new JavaLangThreadStart0Native(view, method, ptaScene.getFieldCurrentThread()).simulate(); + break; + case "": + // new JavaLangReflectArrayGet(method).simulate(); + break; + case "": + // new JavaLangReflectArraySet(method).simulate(); + break; + default: + // System.out.println("Warning: unhandled native method " + sig); + break; + } + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/natives/package-info.java b/sootup.qilin/src/main/java/qilin/core/natives/package-info.java new file mode 100644 index 00000000000..4ee4ce5aeba --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/natives/package-info.java @@ -0,0 +1,7 @@ +package qilin.core.natives; + +/* + * In this package, we currently only handle a subset of native methods that are supported in Doop. + * In the future, we will append more simulations to native methods that are frequently used by + * real-world applications. + * */ diff --git a/sootup.qilin/src/main/java/qilin/core/pag/AllocNode.java b/sootup.qilin/src/main/java/qilin/core/pag/AllocNode.java new file mode 100644 index 00000000000..a1196aa2f3a --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/pag/AllocNode.java @@ -0,0 +1,61 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.pag; + +import qilin.core.context.ContextElement; +import qilin.util.Numberable; +import sootup.core.model.SootMethod; +import sootup.core.types.Type; + +/** + * Represents an allocation site node in the pointer assignment graph. + * + * @author Ondrej Lhotak + */ +public class AllocNode extends Node implements ContextElement, Numberable { + protected Object newExpr; + private final SootMethod method; + + public AllocNode(Object newExpr, Type t, SootMethod m) { + super(t); + this.method = m; + this.newExpr = newExpr; + } + + /** Returns the new expression of this allocation site. */ + public Object getNewExpr() { + return newExpr; + } + + public String toString() { + return "AllocNode " + getNumber() + " " + newExpr + " in method " + method; + } + + public String toString2() { + return newExpr + " in method " + method; + } + + public SootMethod getMethod() { + return method; + } + + public AllocNode base() { + return this; + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/pag/ArrayElement.java b/sootup.qilin/src/main/java/qilin/core/pag/ArrayElement.java new file mode 100644 index 00000000000..40b6d758010 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/pag/ArrayElement.java @@ -0,0 +1,55 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.pag; + +import qilin.util.PTAUtils; +import sootup.core.types.Type; + +/** + * Represents an array element. + * + * @author Ondrej Lhotak + */ +public class ArrayElement implements SparkField { + private static ArrayElement instance = null; + private int number = 0; + + public static ArrayElement v() { + if (instance == null) { + synchronized (ArrayElement.class) { + if (instance == null) { + instance = new ArrayElement(); + } + } + } + return instance; + } + + public final int getNumber() { + return number; + } + + public final void setNumber(int number) { + this.number = number; + } + + public Type getType() { + return PTAUtils.getClassType("java.lang.Object"); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/pag/CallSite.java b/sootup.qilin/src/main/java/qilin/core/pag/CallSite.java new file mode 100644 index 00000000000..c2c7e742b9c --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/pag/CallSite.java @@ -0,0 +1,63 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.pag; + +import qilin.core.context.ContextElement; +import sootup.core.jimple.common.stmt.Stmt; + +/** callsite based context element in the points to analysis. */ +public class CallSite implements ContextElement { + + private final Stmt unit; + + public CallSite(Stmt unit) { + this.unit = unit; + } + + public Stmt getUnit() { + return unit; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((unit == null) ? 0 : unit.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + CallSite other = (CallSite) obj; + if (unit == null) { + return other.unit == null; + } else if (other.unit == null) { + return false; + } else { + return unit.equals(other.unit); + } + } + + public String toString() { + return "CallSite: " + unit; + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/pag/ClassConstantNode.java b/sootup.qilin/src/main/java/qilin/core/pag/ClassConstantNode.java new file mode 100644 index 00000000000..e454f28ae24 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/pag/ClassConstantNode.java @@ -0,0 +1,41 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.pag; + +import qilin.util.PTAUtils; +import sootup.core.jimple.common.constant.ClassConstant; + +/** + * Represents an allocation site node the represents a known java.lang.Class object. + * + * @author Ondrej Lhotak + */ +public class ClassConstantNode extends ConstantNode { + public ClassConstantNode(ClassConstant cc) { + super(cc, PTAUtils.getClassType("java.lang.Class"), null); + } + + public String toString() { + return "ClassConstantNode " + getNumber() + " " + newExpr; + } + + public ClassConstant getClassConstant() { + return (ClassConstant) newExpr; + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/pag/ConstantNode.java b/sootup.qilin/src/main/java/qilin/core/pag/ConstantNode.java new file mode 100644 index 00000000000..11035b9f074 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/pag/ConstantNode.java @@ -0,0 +1,28 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.pag; + +import sootup.core.model.SootMethod; +import sootup.core.types.Type; + +public abstract class ConstantNode extends AllocNode { + protected ConstantNode(Object newExpr, Type t, SootMethod m) { + super(newExpr, t, m); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/pag/ContextAllocNode.java b/sootup.qilin/src/main/java/qilin/core/pag/ContextAllocNode.java new file mode 100644 index 00000000000..7c13993831b --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/pag/ContextAllocNode.java @@ -0,0 +1,45 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.pag; + +import qilin.core.context.Context; + +public class ContextAllocNode extends AllocNode { + private final Context context; + private final AllocNode base; + + public ContextAllocNode(AllocNode base, Context context) { + super(base.getNewExpr(), base.getType(), base.getMethod()); + this.context = context; + this.base = base; + } + + public Context context() { + return context; + } + + @Override + public AllocNode base() { + return base; + } + + public String toString() { + return "ContextAllocNode " + getNumber() + "(" + base + ", " + context + ")"; + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/pag/ContextField.java b/sootup.qilin/src/main/java/qilin/core/pag/ContextField.java new file mode 100644 index 00000000000..5e60ca71aff --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/pag/ContextField.java @@ -0,0 +1,83 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.pag; + +import qilin.CoreConfig; +import qilin.core.context.Context; +import qilin.core.context.ContextElement; +import qilin.core.context.ContextElements; +import qilin.util.PTAUtils; +import sootup.core.types.ArrayType; +import sootup.core.types.Type; + +public class ContextField extends ValNode { + protected Context context; + protected SparkField field; + + public ContextField(Context context, SparkField field) { + super(refineFieldType(context, field)); + this.context = context; + this.field = field; + } + + private static Type refineFieldType(Context context, SparkField field) { + if (!CoreConfig.v().getPtaConfig().preciseArrayElement) { + return PTAUtils.getClassType("java.lang.Object"); + } + if (field instanceof ArrayElement) { + ContextElement[] contextElements = ((ContextElements) context).getElements(); + if (contextElements.length > 0) { + Type baseHeapType = ((AllocNode) ((ContextElements) context).getElements()[0]).getType(); + if (baseHeapType instanceof ArrayType) { + ArrayType arrayType = (ArrayType) baseHeapType; + return arrayType.getElementType(); + } else { + throw new RuntimeException(baseHeapType + " is not an array type."); + } + } else { + throw new RuntimeException("Context does not have any elements:" + context + ";" + field); + } + } + return field.getType(); + } + + /** Returns the base AllocNode. */ + public Context getContext() { + return context; + } + + public boolean hasBase() { + ContextElements ctxs = (ContextElements) context; + return ctxs.size() > 0; + } + + public AllocNode getBase() { + ContextElements ctxs = (ContextElements) context; + return (AllocNode) ctxs.getElements()[0]; + } + + /** Returns the field of this node. */ + public SparkField getField() { + return field; + } + + public String toString() { + return "ContextField " + getNumber() + " " + getBase() + "." + field; + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/pag/ContextMethod.java b/sootup.qilin/src/main/java/qilin/core/pag/ContextMethod.java new file mode 100644 index 00000000000..e1a48a0243a --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/pag/ContextMethod.java @@ -0,0 +1,57 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.pag; + +import qilin.core.context.Context; +import sootup.core.model.SootMethod; + +public final class ContextMethod { + private final SootMethod method; + private final Context context; + + public SootMethod method() { + return this.method; + } + + public Context context() { + return this.context; + } + + public ContextMethod(SootMethod method, Context context) { + this.method = method; + this.context = context; + } + + public int hashCode() { + return this.method.hashCode() + this.context.hashCode(); + } + + public boolean equals(Object o) { + if (!(o instanceof ContextMethod)) { + return false; + } else { + ContextMethod other = (ContextMethod) o; + return this.method.equals(other.method) && this.context.equals(other.context); + } + } + + public String toString() { + return "Method " + this.method + " in context " + this.context; + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/pag/ContextVarNode.java b/sootup.qilin/src/main/java/qilin/core/pag/ContextVarNode.java new file mode 100644 index 00000000000..2d87059bc96 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/pag/ContextVarNode.java @@ -0,0 +1,62 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.pag; + +import qilin.core.context.Context; +import sootup.core.model.SootMethod; + +public class ContextVarNode extends VarNode { + private final Context context; + private final VarNode base; + + public ContextVarNode(VarNode base, Context context) { + super(base.getVariable(), base.getType()); + this.context = context; + this.base = base; + } + + @Override + public boolean isInterProcTarget() { + return base.isInterProcTarget(); + } + + @Override + public boolean isInterProcSource() { + return base.isInterProcSource(); + } + + @Override + public Context context() { + return context; + } + + @Override + public VarNode base() { + return base; + } + + @Override + public SootMethod getMethod() { + return base.getMethod(); + } + + public String toString() { + return "ContextVarNode " + getNumber() + "(" + base + ", " + context + ")"; + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/pag/ExceptionThrowSite.java b/sootup.qilin/src/main/java/qilin/core/pag/ExceptionThrowSite.java new file mode 100644 index 00000000000..bb74bab0961 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/pag/ExceptionThrowSite.java @@ -0,0 +1,61 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.pag; + +import java.util.Objects; +import sootup.core.jimple.common.stmt.Stmt; + +public class ExceptionThrowSite { + private final Stmt unit; + private final VarNode throwNode; + private final ContextMethod container; + + public ExceptionThrowSite(VarNode throwNode, Stmt unit, ContextMethod container) { + this.unit = unit; + this.container = container; + this.throwNode = throwNode; + } + + public ContextMethod container() { + return container; + } + + public VarNode getThrowNode() { + return throwNode; + } + + public Stmt getUnit() { + return unit; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ExceptionThrowSite that = (ExceptionThrowSite) o; + return Objects.equals(unit, that.unit) + && Objects.equals(throwNode, that.throwNode) + && Objects.equals(container, that.container); + } + + @Override + public int hashCode() { + return Objects.hash(unit, throwNode, container); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/pag/Field.java b/sootup.qilin/src/main/java/qilin/core/pag/Field.java new file mode 100644 index 00000000000..945ce7dc49b --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/pag/Field.java @@ -0,0 +1,59 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.pag; + +import java.util.Objects; +import sootup.core.model.SootField; +import sootup.core.types.Type; + +/** a wrapper of normal field. */ +public class Field implements SparkField { + private final SootField field; + + public Field(SootField sf) { + this.field = sf; + } + + @Override + public Type getType() { + return field.getType(); + } + + public SootField getField() { + return field; + } + + @Override + public String toString() { + return "FieldNode " + field; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Field field1 = (Field) o; + return field.equals(field1.field); + } + + @Override + public int hashCode() { + return Objects.hash(field); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/pag/FieldRefNode.java b/sootup.qilin/src/main/java/qilin/core/pag/FieldRefNode.java new file mode 100644 index 00000000000..8d73bf3ee2d --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/pag/FieldRefNode.java @@ -0,0 +1,52 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.pag; + +import qilin.util.Numberable; + +/** + * Represents a field reference node in the pointer assignment graph. + * + * @author Ondrej Lhotak + */ +public class FieldRefNode extends Node implements Numberable { + protected VarNode base; + protected SparkField field; + + public FieldRefNode(VarNode base, SparkField field) { + super(field.getType()); + this.base = base; + this.field = field; + base.addField(this, field); + } + + /** Returns the base of this field reference. */ + public VarNode getBase() { + return base; + } + + /** Returns the field of this field reference. */ + public SparkField getField() { + return field; + } + + public String toString() { + return "FieldRefNode " + getNumber() + " " + base + "." + field; + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/pag/FieldValNode.java b/sootup.qilin/src/main/java/qilin/core/pag/FieldValNode.java new file mode 100644 index 00000000000..16f14fcda06 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/pag/FieldValNode.java @@ -0,0 +1,34 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.pag; + +public class FieldValNode extends ValNode { + // note, field could only be ArrayElement or Field type. + // Parm should be a local variable in some methods. + private final SparkField field; + + public FieldValNode(SparkField field) { + super(field.getType()); + this.field = field; + } + + public SparkField getField() { + return field; + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/pag/GlobalVarNode.java b/sootup.qilin/src/main/java/qilin/core/pag/GlobalVarNode.java new file mode 100644 index 00000000000..0c28c897c6f --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/pag/GlobalVarNode.java @@ -0,0 +1,48 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.pag; + +import sootup.core.model.SootMethod; +import sootup.core.types.Type; + +/** + * Represents a simple variable node in the pointer assignment graph that is not associated with any + * particular method invocation. + * + * @author Ondrej Lhotak + */ +public class GlobalVarNode extends VarNode { + public GlobalVarNode(Object variable, Type t) { + super(variable, t); + } + + @Override + public VarNode base() { + return this; + } + + @Override + public SootMethod getMethod() { + return null; + } + + public String toString() { + return "GlobalVarNode " + getNumber() + " " + variable; + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/pag/LocalVarNode.java b/sootup.qilin/src/main/java/qilin/core/pag/LocalVarNode.java new file mode 100644 index 00000000000..5cdf6c4249c --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/pag/LocalVarNode.java @@ -0,0 +1,62 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.pag; + +import sootup.core.model.SootMethod; +import sootup.core.types.Type; + +/** @author Ondrej Lhotak */ +public class LocalVarNode extends VarNode { + protected SootMethod method; + + public LocalVarNode(Object variable, Type t, SootMethod m) { + super(variable, t); + this.method = m; + } + + public SootMethod getMethod() { + return method; + } + + public String toString() { + return "LocalVarNode " + getNumber() + " " + variable + " " + method; + } + + /** Returns true if this VarNode represents the THIS pointer */ + public boolean isThis() { + if (variable instanceof Parm) { + Parm parm = (Parm) variable; + return parm.isThis(); + } + return false; + } + + public boolean isReturn() { + if (variable instanceof Parm) { + Parm parm = (Parm) variable; + return parm.isReturn(); + } + return false; + } + + @Override + public VarNode base() { + return this; + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/pag/MergedNewExpr.java b/sootup.qilin/src/main/java/qilin/core/pag/MergedNewExpr.java new file mode 100644 index 00000000000..e21a1ed7c27 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/pag/MergedNewExpr.java @@ -0,0 +1,36 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.pag; + +import java.util.Map; +import qilin.util.DataFactory; +import sootup.core.types.ReferenceType; + +public class MergedNewExpr { + private final ReferenceType type; + private static final Map map = DataFactory.createMap(); + + private MergedNewExpr(ReferenceType type) { + this.type = type; + } + + public static MergedNewExpr v(ReferenceType type) { + return map.computeIfAbsent(type, k -> new MergedNewExpr(type)); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/pag/MethodPAG.java b/sootup.qilin/src/main/java/qilin/core/pag/MethodPAG.java new file mode 100644 index 00000000000..6c770f31ce4 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/pag/MethodPAG.java @@ -0,0 +1,182 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.pag; + +import java.util.*; +import qilin.CoreConfig; +import qilin.core.PTAScene; +import qilin.core.builder.MethodNodeFactory; +import qilin.util.DataFactory; +import qilin.util.PTAUtils; +import qilin.util.queue.ChunkedQueue; +import qilin.util.queue.QueueReader; +import sootup.core.jimple.Jimple; +import sootup.core.jimple.basic.Trap; +import sootup.core.jimple.common.ref.JStaticFieldRef; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.Body; +import sootup.core.model.SootClass; +import sootup.core.model.SootField; +import sootup.core.model.SootMethod; + +/** + * Part of a pointer assignment graph for a single method. + * + * @author Ondrej Lhotak + */ +public class MethodPAG { + private final ChunkedQueue internalEdges = new ChunkedQueue<>(); + private final QueueReader internalReader = internalEdges.reader(); + private final Set clinits = DataFactory.createSet(); + private final Collection invokeStmts = DataFactory.createSet(); + public Body body; + + /** + * Since now the exception analysis is handled on-the-fly, we should record the exception edges + * explicitly for Eagle and Turner. + */ + private final Map> exceptionEdges = DataFactory.createMap(); + + protected MethodNodeFactory nodeFactory; + protected final PTAScene ptaScene; + SootMethod method; + /* + * List[i-1] is wrappered in List[i]. + * We have to extend the following structure from Map> to + * Map>> because there exists cases where the same + * node are thrown more than once and lies in different catch blocks. + * */ + public final Map> stmt2wrapperedTraps = DataFactory.createMap(); + public final Map>> node2wrapperedTraps = DataFactory.createMap(); + + public MethodPAG(PAG pag, SootMethod m, Body body) { + this.ptaScene = pag.getPta().getScene(); + this.method = m; + this.nodeFactory = new MethodNodeFactory(pag, this); + this.body = body; + build(); + } + + public SootMethod getMethod() { + return method; + } + + public MethodNodeFactory nodeFactory() { + return nodeFactory; + } + + public Collection getInvokeStmts() { + return invokeStmts; + } + + public boolean addCallStmt(Stmt unit) { + return this.invokeStmts.add(unit); + } + + protected void build() { + // this method is invalid but exists in pmd-deps.jar + if (method + .getSignature() + .toString() + .equals( + "")) { + return; + } + buildException(); + buildNormal(); + addMiscEdges(); + } + + protected void buildNormal() { + if (method.isStatic()) { + if (!PTAUtils.isFakeMainMethod(method)) { + SootClass sc = ptaScene.getView().getClass(method.getDeclaringClassType()).get(); + nodeFactory.clinitsOf(sc).forEach(this::addTriggeredClinit); + } + } + for (Stmt unit : body.getStmts()) { + try { + nodeFactory.handleStmt(unit); + } catch (Exception e) { + System.out.println("Warning:" + e + " in " + this.getClass()); + } + } + } + + protected void buildException() { + // we use the same logic as doop (library/exceptions/precise.logic). + if (!CoreConfig.v().getPtaConfig().preciseExceptions) { + return; + } + // List traps = body.getTraps(); + // // List units = body.getStmts(); + // Set inTraps = DataFactory.createSet(); + } + + private void addStmtTrap(Node src, Stmt stmt, Trap trap) { + Map> stmt2Traps = + node2wrapperedTraps.computeIfAbsent(src, k -> DataFactory.createMap()); + List trapList = stmt2Traps.computeIfAbsent(stmt, k -> DataFactory.createList()); + trapList.add(trap); + stmt2wrapperedTraps.computeIfAbsent(stmt, k -> DataFactory.createList()).add(trap); + } + + protected void addMiscEdges() { + if (method + .getSignature() + .toString() + .equals( + "(java.lang.Object,java.lang.ref.ReferenceQueue)>")) { + // Implements the special status of java.lang.ref.Reference just as in Doop + // (library/reference.logic). + SootClass sootClass = ptaScene.getSootClass("java.lang.ref.Reference"); + SootField sf = sootClass.getField("pending").get(); + JStaticFieldRef sfr = Jimple.newStaticFieldRef(sf.getSignature()); + addInternalEdge(nodeFactory.caseThis(), nodeFactory.getNode(sfr)); + } + } + + public void addInternalEdge(Node src, Node dst) { + if (src == null) { + return; + } + internalEdges.add(src); + internalEdges.add(dst); + } + + public QueueReader getInternalReader() { + return internalReader; + } + + public void addTriggeredClinit(SootMethod clinit) { + clinits.add(clinit); + } + + public Iterator triggeredClinits() { + return clinits.iterator(); + } + + public void addExceptionEdge(Node from, Node to) { + this.exceptionEdges.computeIfAbsent(from, k -> DataFactory.createSet()).add(to); + } + + public Map> getExceptionEdges() { + return this.exceptionEdges; + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/pag/Node.java b/sootup.qilin/src/main/java/qilin/core/pag/Node.java new file mode 100644 index 00000000000..9cfa11c0815 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/pag/Node.java @@ -0,0 +1,61 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.pag; + +import qilin.core.sets.DoublePointsToSet; +import qilin.util.Numberable; +import sootup.core.types.Type; + +/** + * Represents every node in the pointer assignment graph. + * + * @author Ondrej Lhotak + */ +public class Node implements Numberable { + protected Type type; + protected DoublePointsToSet p2set; + private int number = 0; + + /** Creates a new node of pointer assignment graph pag, with type type. */ + protected Node(Type type) { + this.type = type; + } + + @Override + public final int hashCode() { + return number; + } + + public final boolean equals(Object other) { + return this == other; + } + + /** Returns the declared type of this node, null for unknown. */ + public Type getType() { + return type; + } + + public final int getNumber() { + return number; + } + + public final void setNumber(int number) { + this.number = number; + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/pag/PAG.java b/sootup.qilin/src/main/java/qilin/core/pag/PAG.java new file mode 100644 index 00000000000..a3e6503a789 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/pag/PAG.java @@ -0,0 +1,646 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.pag; + +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; +import qilin.CoreConfig; +import qilin.core.PTA; +import qilin.core.PointsToAnalysis; +import qilin.core.builder.CallGraphBuilder; +import qilin.core.context.Context; +import qilin.core.natives.NativeMethodDriver; +import qilin.core.reflection.NopReflectionModel; +import qilin.core.reflection.ReflectionModel; +import qilin.core.reflection.TamiflexModel; +import qilin.util.ArrayNumberer; +import qilin.util.DataFactory; +import qilin.util.PTAUtils; +import qilin.util.Triple; +import qilin.util.queue.ChunkedQueue; +import qilin.util.queue.QueueReader; +import sootup.core.graph.MutableStmtGraph; +import sootup.core.jimple.Jimple; +import sootup.core.jimple.basic.LValue; +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.basic.StmtPositionInfo; +import sootup.core.jimple.basic.Value; +import sootup.core.jimple.common.constant.ClassConstant; +import sootup.core.jimple.common.constant.IntConstant; +import sootup.core.jimple.common.constant.StringConstant; +import sootup.core.jimple.common.expr.AbstractInvokeExpr; +import sootup.core.jimple.common.expr.JStaticInvokeExpr; +import sootup.core.jimple.common.stmt.JAssignStmt; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.Body; +import sootup.core.model.SootClass; +import sootup.core.model.SootMethod; +import sootup.core.signatures.FieldSignature; +import sootup.core.types.ArrayType; +import sootup.core.types.ClassType; +import sootup.core.types.Type; +import sootup.core.views.View; +import sootup.java.core.language.JavaJimple; + +/** + * Pointer assignment graph. + * + * @author Ondrej Lhotak + */ +public class PAG { + protected final NativeMethodDriver nativeDriver; + protected final ReflectionModel reflectionModel; + + // ========================= context-sensitive nodes ================================= + protected final Map> contextVarNodeMap; + protected final Map> contextAllocNodeMap; + protected final Map> contextMethodMap; + protected final Map> addedContexts; + protected final Map> contextFieldMap; + + // ==========================data========================= + protected ArrayNumberer allocNodeNumberer = new ArrayNumberer<>(); + protected ArrayNumberer valNodeNumberer = new ArrayNumberer<>(); + protected ArrayNumberer fieldRefNodeNumberer = new ArrayNumberer<>(); + private static AtomicInteger maxFinishNumber = new AtomicInteger(0); + + // ========================= ir to Node ============================================== + protected final Map valToAllocNode; + protected final Map valToValNode; + protected final Map methodToPag; + protected final Set globals; + protected final Set> locals; + // ==========================outer objects============================== + protected ChunkedQueue edgeQueue; + + protected final Map> simple; + protected final Map> simpleInv; + protected final Map> load; + protected final Map> loadInv; + protected final Map> alloc; + protected final Map> allocInv; + protected final Map> store; + protected final Map> storeInv; + + protected final PTA pta; + + public PAG(PTA pta) { + this.pta = pta; + this.simple = DataFactory.createMap(); + this.simpleInv = DataFactory.createMap(); + this.load = DataFactory.createMap(); + this.loadInv = DataFactory.createMap(); + this.alloc = DataFactory.createMap(); + this.allocInv = DataFactory.createMap(); + this.store = DataFactory.createMap(); + this.storeInv = DataFactory.createMap(); + this.nativeDriver = new NativeMethodDriver(pta.getScene()); + this.reflectionModel = createReflectionModel(); + this.contextVarNodeMap = DataFactory.createMap(16000); + this.contextAllocNodeMap = DataFactory.createMap(6000); + this.contextMethodMap = DataFactory.createMap(6000); + this.addedContexts = DataFactory.createMap(); + this.contextFieldMap = DataFactory.createMap(6000); + this.valToAllocNode = DataFactory.createMap(10000); + this.valToValNode = DataFactory.createMap(100000); + this.methodToPag = DataFactory.createMap(); + this.globals = DataFactory.createSet(100000); + this.locals = DataFactory.createSet(100000); + } + + public void setEdgeQueue(ChunkedQueue edgeQueue) { + this.edgeQueue = edgeQueue; + } + + public Map> getAlloc() { + return alloc; + } + + public Map> getSimple() { + return simple; + } + + public Map> getSimpleInv() { + return simpleInv; + } + + public Map> getLoad() { + return load; + } + + public Map> getStoreInv() { + return storeInv; + } + + public PTA getPta() { + return this.pta; + } + + public CallGraphBuilder getCgb() { + return pta.getCgb(); + } + + public QueueReader edgeReader() { + return edgeQueue.reader(); + } + + // =======================add edge=============================== + protected boolean addToMap(Map> m, K key, V value) { + Set valueList = m.computeIfAbsent(key, k -> DataFactory.createSet(4)); + return valueList.add(value); + } + + private boolean addAllocEdge(AllocNode from, VarNode to) { + if (addToMap(alloc, from, to)) { + addToMap(allocInv, to, from); + return true; + } + return false; + } + + private boolean addSimpleEdge(ValNode from, ValNode to) { + if (addToMap(simple, from, to)) { + addToMap(simpleInv, to, from); + return true; + } + return false; + } + + private boolean addStoreEdge(VarNode from, FieldRefNode to) { + if (addToMap(storeInv, to, from)) { + addToMap(store, from, to); + return true; + } + return false; + } + + private boolean addLoadEdge(FieldRefNode from, VarNode to) { + if (addToMap(load, from, to)) { + addToMap(loadInv, to, from); + return true; + } + return false; + } + + public void addGlobalPAGEdge(Node from, Node to) { + from = pta.parameterize(from, pta.emptyContext()); + to = pta.parameterize(to, pta.emptyContext()); + addEdge(from, to); + } + + /** Adds an edge to the graph, returning false if it was already there. */ + public final void addEdge(Node from, Node to) { + if (addEdgeIntenal(from, to)) { + edgeQueue.add(from); + edgeQueue.add(to); + } + } + + private boolean addEdgeIntenal(Node from, Node to) { + if (from instanceof ValNode) { + if (to instanceof ValNode) { + return addSimpleEdge((ValNode) from, (ValNode) to); + } else { + return addStoreEdge((VarNode) from, (FieldRefNode) to); + } + } else if (from instanceof FieldRefNode) { + return addLoadEdge((FieldRefNode) from, (VarNode) to); + } else { + AllocNode heap = (AllocNode) from; + return addAllocEdge(heap, (VarNode) to); + } + } + + // ======================lookups=========================== + protected Set lookup(Map> m, K key) { + return m.getOrDefault(key, Collections.emptySet()); + } + + public Set allocLookup(AllocNode key) { + return lookup(alloc, key); + } + + public Set allocInvLookup(VarNode key) { + return lookup(allocInv, key); + } + + public Set simpleLookup(ValNode key) { + return lookup(simple, key); + } + + public Set simpleInvLookup(ValNode key) { + return lookup(simpleInv, key); + } + + public Set loadInvLookup(VarNode key) { + return lookup(loadInv, key); + } + + public Set loadLookup(FieldRefNode key) { + return lookup(load, key); + } + + public Set storeLookup(VarNode key) { + return lookup(store, key); + } + + public Set storeInvLookup(FieldRefNode key) { + return lookup(storeInv, key); + } + + public static int nextFinishNumber() { + return maxFinishNumber.incrementAndGet(); + } + + public ArrayNumberer getAllocNodeNumberer() { + return allocNodeNumberer; + } + + public ArrayNumberer getFieldRefNodeNumberer() { + return fieldRefNodeNumberer; + } + + public ArrayNumberer getValNodeNumberer() { + return valNodeNumberer; + } + + public Collection getValNodes() { + return valToValNode.values(); + } + + public Collection getAllocNodes() { + return valToAllocNode.values(); + } + + public Set getGlobalPointers() { + return globals; + } + + public Set> getLocalPointers() { + return locals; + } + + /** Finds the ValNode for the variable value, or returns null. */ + public ValNode findValNode(Object value, SootMethod containingMethod) { + if (value instanceof Local) { + Local local = (Local) value; + Triple localTriple = + new Triple<>(containingMethod, local, local.getType()); + return valToValNode.get(localTriple); + } else { + return valToValNode.get(value); + } + } + + public AllocNode findAllocNode(Object obj) { + return valToAllocNode.get(obj); + } + + // ==========================create nodes================================== + public AllocNode makeAllocNode(Object newExpr, Type type, SootMethod m) { + if (type instanceof ClassType) { + ClassType rt = (ClassType) type; + View view = pta.getView(); + Optional osc = view.getClass(rt); + if (osc.isPresent() && osc.get().isAbstract()) { + boolean usesReflectionLog = CoreConfig.v().getAppConfig().REFLECTION_LOG != null; + if (!usesReflectionLog) { + throw new RuntimeException("Attempt to create allocnode with abstract type " + rt); + } + } + } + AllocNode ret = valToAllocNode.get(newExpr); + if (ret == null) { + valToAllocNode.put(newExpr, ret = new AllocNode(newExpr, type, m)); + allocNodeNumberer.add(ret); + } else if (!(ret.getType().equals(type))) { + throw new RuntimeException( + "NewExpr " + newExpr + " of type " + type + " previously had type " + ret.getType()); + } + return ret; + } + + public AllocNode makeStringConstantNode(StringConstant sc) { + StringConstant stringConstant = sc; + if (!CoreConfig.v().getPtaConfig().stringConstants) { + stringConstant = JavaJimple.getInstance().newStringConstant(PointsToAnalysis.STRING_NODE); + } + AllocNode ret = valToAllocNode.get(stringConstant); + if (ret == null) { + valToAllocNode.put(stringConstant, ret = new StringConstantNode(stringConstant)); + allocNodeNumberer.add(ret); + } + return ret; + } + + public AllocNode makeClassConstantNode(ClassConstant cc) { + AllocNode ret = valToAllocNode.get(cc); + if (ret == null) { + valToAllocNode.put(cc, ret = new ClassConstantNode(cc)); + allocNodeNumberer.add(ret); + } + return ret; + } + + /** Finds or creates the GlobalVarNode for the variable value, of type type. */ + public GlobalVarNode makeGlobalVarNode(Object value, Type type) { + // value could only be a StringConstant, ClassConstant, or SootField. + GlobalVarNode ret = (GlobalVarNode) valToValNode.get(value); + if (ret == null) { + ret = + (GlobalVarNode) valToValNode.computeIfAbsent(value, k -> new GlobalVarNode(value, type)); + valNodeNumberer.add(ret); + if (value instanceof FieldSignature) { + globals.add((FieldSignature) value); + } + } else if (!(ret.getType().equals(type))) { + throw new RuntimeException( + "Value " + value + " of type " + type + " previously had type " + ret.getType()); + } + return ret; + } + + /** Finds or creates the LocalVarNode for the variable value, of type type. */ + public LocalVarNode makeLocalVarNode(Object value, Type type, SootMethod method) { + Triple localTriple = new Triple<>(method, value, type); + LocalVarNode ret = (LocalVarNode) valToValNode.get(localTriple); + if (ret == null) { + valToValNode.put(localTriple, ret = new LocalVarNode(value, type, method)); + valNodeNumberer.add(ret); + if (value instanceof Local) { + Local local = (Local) value; + locals.add(new Triple<>(method, local, type)); + } + } else if (!(ret.getType().equals(type))) { + throw new RuntimeException( + "Value " + value + " of type " + type + " previously had type " + ret.getType()); + } + return ret; + } + + /** + * Finds or creates the FieldVarNode for the Java field or array element. Treat Java field and + * array element as normal local variable. + */ + public FieldValNode makeFieldValNode(SparkField field) { + FieldValNode ret = (FieldValNode) valToValNode.get(field); + if (ret == null) { + valToValNode.put(field, ret = new FieldValNode(field)); + valNodeNumberer.add(ret); + } + return ret; + } + + /** Finds or creates the FieldRefNode for base variable base and field field, of type type. */ + public FieldRefNode makeFieldRefNode(VarNode base, SparkField field) { + FieldRefNode ret = base.dot(field); + if (ret == null) { + ret = new FieldRefNode(base, field); + fieldRefNodeNumberer.add(ret); + } + return ret; + } + + /** Finds or creates the ContextVarNode for base variable base and context. */ + public ContextVarNode makeContextVarNode(VarNode base, Context context) { + Map contextMap = + contextVarNodeMap.computeIfAbsent(base, k1 -> DataFactory.createMap()); + ContextVarNode ret = contextMap.get(context); + if (ret == null) { + contextMap.put(context, ret = new ContextVarNode(base, context)); + valNodeNumberer.add(ret); + } + return ret; + } + + /** Finds or creates the ContextAllocNode for base alloc site and context. */ + public ContextAllocNode makeContextAllocNode(AllocNode allocNode, Context context) { + Map contextMap = + contextAllocNodeMap.computeIfAbsent(allocNode, k1 -> DataFactory.createMap()); + ContextAllocNode ret = contextMap.get(context); + if (ret == null) { + contextMap.put(context, ret = new ContextAllocNode(allocNode, context)); + allocNodeNumberer.add(ret); + } + return ret; + } + + /** Finds or creates the ContextMethod for method and context. */ + public ContextMethod makeContextMethod(Context context, SootMethod method) { + Map contextMap = + contextMethodMap.computeIfAbsent(method, k1 -> DataFactory.createMap()); + return contextMap.computeIfAbsent(context, k -> new ContextMethod(method, context)); + } + + public AllocNode getAllocNode(Object val) { + return valToAllocNode.get(val); + } + + public Map> getMethod2ContextsMap() { + return addedContexts; + } + + public Collection getContextFields() { + return contextFieldMap.values().stream() + .flatMap(m -> m.values().stream()) + .collect(Collectors.toSet()); + } + + public Map> getContextVarNodeMap() { + return contextVarNodeMap; + } + + public Map> getContextAllocNodeMap() { + return contextAllocNodeMap; + } + + public Map> getContextMethodMap() { + return contextMethodMap; + } + + public Map> getContextFieldVarNodeMap() { + return contextFieldMap; + } + + public ContextField makeContextField(Context context, FieldValNode fieldValNode) { + SparkField field = fieldValNode.getField(); + Map field2odotf = + contextFieldMap.computeIfAbsent(context, k -> DataFactory.createMap()); + ContextField ret = field2odotf.get(field); + if (ret == null) { + field2odotf.put(field, ret = new ContextField(context, field)); + valNodeNumberer.add(ret); + } + return ret; + } + + public Collection getVarNodes(SootMethod m, Local local) { + LocalVarNode lvn = findLocalVarNode(m, local, local.getType()); + Map subMap = contextVarNodeMap.get(lvn); + if (subMap == null) { + return Collections.emptySet(); + } + return new HashSet<>(subMap.values()); + } + + // ===================find nodes============================== + + /** Finds the GlobalVarNode for the variable value, or returns null. */ + public GlobalVarNode findGlobalVarNode(Object value) { + if (value instanceof Local) { + System.out.println("Warning: find global varnode for local value:" + value); + return null; + } else { + return (GlobalVarNode) valToValNode.get(value); + } + } + + /** Finds the LocalVarNode for the variable value, or returns null. */ + public LocalVarNode findLocalVarNode(SootMethod m, Object value, Type type) { + Triple key = new Triple<>(m, value, type); + ValNode ret = valToValNode.get(key); + if (ret instanceof LocalVarNode) { + return (LocalVarNode) ret; + } + return null; + } + + /** Finds the ContextVarNode for base variable value and context context, or returns null. */ + public ContextVarNode findContextVarNode(SootMethod m, Local baseValue, Context context) { + LocalVarNode lvn = findLocalVarNode(m, baseValue, baseValue.getType()); + Map contextMap = contextVarNodeMap.get(lvn); + return contextMap == null ? null : contextMap.get(context); + } + + protected ReflectionModel createReflectionModel() { + ReflectionModel model; + if (CoreConfig.v().getAppConfig().REFLECTION_LOG != null + && CoreConfig.v().getAppConfig().REFLECTION_LOG.length() > 0) { + model = new TamiflexModel(pta.getScene()); + } else { + model = new NopReflectionModel(pta.getScene()); + } + return model; + } + + public MethodPAG getMethodPAG(SootMethod m) { + if (methodToPag.containsKey(m)) { + return methodToPag.get(m); + } + if (m.isConcrete()) { + reflectionModel.buildReflection(m); + } + if (m.isNative()) { + nativeDriver.buildNative(m); + } else { + // we will revert these back in the future. + /* + * To keep same with Doop, we move the simulation of + * + * directly to its caller methods. + * */ + if (pta.getScene().arraycopyBuilt.add(m)) { + handleArrayCopy(m); + } + } + Body body = PTAUtils.getMethodBody(m); + return methodToPag.computeIfAbsent(m, k -> new MethodPAG(this, m, body)); + } + + private void handleArrayCopy(SootMethod method) { + Map> newUnits = DataFactory.createMap(); + Body body = PTAUtils.getMethodBody(method); + Body.BodyBuilder builder = Body.builder(body, Collections.emptySet()); + int localCount = body.getLocalCount(); + for (Stmt s : body.getStmts()) { + if (s.containsInvokeExpr()) { + AbstractInvokeExpr invokeExpr = s.getInvokeExpr(); + if (invokeExpr instanceof JStaticInvokeExpr) { + JStaticInvokeExpr sie = (JStaticInvokeExpr) invokeExpr; + String sig = sie.getMethodSignature().toString(); + if (sig.equals( + "")) { + Value srcArr = sie.getArg(0); + if (PTAUtils.isPrimitiveArrayType(srcArr.getType())) { + continue; + } + Type objType = PTAUtils.getClassType("java.lang.Object"); + if (srcArr.getType() == objType) { + Local localSrc = + Jimple.newLocal("intermediate/" + (localCount++), new ArrayType(objType, 1)); + builder.addLocal(localSrc); + newUnits + .computeIfAbsent(s, k -> new HashSet<>()) + .add(new JAssignStmt(localSrc, srcArr, StmtPositionInfo.getNoStmtPositionInfo())); + srcArr = localSrc; + } + Value dstArr = sie.getArg(2); + if (PTAUtils.isPrimitiveArrayType(dstArr.getType())) { + continue; + } + if (dstArr.getType() == objType) { + Local localDst = + Jimple.newLocal("intermediate/" + (localCount++), new ArrayType(objType, 1)); + builder.addLocal(localDst); + newUnits + .computeIfAbsent(s, k -> new HashSet<>()) + .add(new JAssignStmt(localDst, dstArr, StmtPositionInfo.getNoStmtPositionInfo())); + dstArr = localDst; + } + Value src = + JavaJimple.getInstance().newArrayRef((Local) srcArr, IntConstant.getInstance(0)); + LValue dst = + JavaJimple.getInstance().newArrayRef((Local) dstArr, IntConstant.getInstance(0)); + Local local = + Jimple.newLocal( + "nativeArrayCopy" + (localCount++), PTAUtils.getClassType("java.lang.Object")); + builder.addLocal(local); + newUnits + .computeIfAbsent(s, k -> DataFactory.createSet()) + .add(new JAssignStmt(local, src, StmtPositionInfo.getNoStmtPositionInfo())); + newUnits + .computeIfAbsent(s, k -> DataFactory.createSet()) + .add(new JAssignStmt(dst, local, StmtPositionInfo.getNoStmtPositionInfo())); + } + } + } + } + + final MutableStmtGraph stmtGraph = builder.getStmtGraph(); + for (Stmt unit : newUnits.keySet()) { + for (JAssignStmt succ : newUnits.get(unit)) { + stmtGraph.insertBefore(unit, succ); + } + } + PTAUtils.updateMethodBody(method, builder.build()); + } + + public void resetPointsToSet() { + this.addedContexts.clear(); + contextVarNodeMap.values().stream() + .flatMap(m -> m.values().stream()) + .forEach(ValNode::discardP2Set); + contextFieldMap.values().stream() + .flatMap(m -> m.values().stream()) + .forEach(ValNode::discardP2Set); + valToValNode.values().forEach(ValNode::discardP2Set); + addedContexts.clear(); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/pag/Parm.java b/sootup.qilin/src/main/java/qilin/core/pag/Parm.java new file mode 100644 index 00000000000..e7a3b605bdc --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/pag/Parm.java @@ -0,0 +1,81 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.pag; + +import java.util.Objects; +import qilin.core.PointsToAnalysis; +import sootup.core.model.SootMethod; + +/** + * Represents a method parameter. + * + * @author Ondrej Lhotak + */ +public class Parm { + private final int index; + private final SootMethod method; + + public Parm(SootMethod m, int i) { + index = i; + method = m; + } + + public String toString() { + if (index == PointsToAnalysis.THIS_NODE) { + return "Parm THIS_NODE to " + method; + } else if (index == PointsToAnalysis.RETURN_NODE) { + return "Parm RETURN to " + method; + } else { + return "Parm " + index + " to " + method; + } + } + + public int getIndex() { + return index; + } + + public boolean isThis() { + return index == PointsToAnalysis.THIS_NODE; + } + + public boolean isReturn() { + return index == PointsToAnalysis.RETURN_NODE; + } + + public boolean isThrowRet() { + return index == PointsToAnalysis.THROW_NODE; + } + + public SootMethod method() { + return method; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Parm parm = (Parm) o; + return index == parm.index && method.equals(parm.method); + } + + @Override + public int hashCode() { + return Objects.hash(index, method); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/pag/SparkField.java b/sootup.qilin/src/main/java/qilin/core/pag/SparkField.java new file mode 100644 index 00000000000..e23c7dc9d91 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/pag/SparkField.java @@ -0,0 +1,35 @@ +package qilin.core.pag; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 2002 Ondrej Lhotak + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import sootup.core.types.Type; + +/** + * Represents a field. + * + * @author Ondrej Lhotak + */ +// public interface SparkField extends Numberable { +public interface SparkField { + public Type getType(); +} diff --git a/sootup.qilin/src/main/java/qilin/core/pag/StringConstantNode.java b/sootup.qilin/src/main/java/qilin/core/pag/StringConstantNode.java new file mode 100644 index 00000000000..51d70927f9c --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/pag/StringConstantNode.java @@ -0,0 +1,41 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.pag; + +import qilin.util.PTAUtils; +import sootup.core.jimple.common.constant.StringConstant; + +/** + * Represents an allocation site node the represents a constant string. + * + * @author Ondrej Lhotak + */ +public class StringConstantNode extends ConstantNode { + public StringConstantNode(StringConstant sc) { + super(sc, PTAUtils.getClassType("java.lang.String"), null); + } + + public String toString() { + return "StringConstantNode " + getNumber() + " " + newExpr; + } + + public String getString() { + return ((StringConstant) newExpr).getValue(); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/pag/ValNode.java b/sootup.qilin/src/main/java/qilin/core/pag/ValNode.java new file mode 100644 index 00000000000..df92e0b9143 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/pag/ValNode.java @@ -0,0 +1,51 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.pag; + +import qilin.core.sets.DoublePointsToSet; +import qilin.util.Numberable; +import sootup.core.types.Type; + +/** Represents a simple of pointer node in the pointer assignment graph. */ +public class ValNode extends Node implements Comparable, Numberable { + + protected ValNode(Type t) { + super(t); + } + + public int compareTo(Object o) { + ValNode other = (ValNode) o; + return other.getNumber() - this.getNumber(); + } + + /** Returns the points-to set for this node. */ + public DoublePointsToSet getP2Set() { + if (p2set != null) { + return p2set; + } else { + p2set = new DoublePointsToSet(); + return p2set; + } + } + + /** Delete current points-to set and make a new one */ + public void discardP2Set() { + p2set = null; + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/pag/VarNode.java b/sootup.qilin/src/main/java/qilin/core/pag/VarNode.java new file mode 100644 index 00000000000..713be566982 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/pag/VarNode.java @@ -0,0 +1,122 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.pag; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import qilin.core.context.Context; +import qilin.util.DataFactory; +import sootup.core.model.SootMethod; +import sootup.core.types.ReferenceType; +import sootup.core.types.Type; + +/** + * Represents a simple variable node in the pointer assignment graph. + * + * @author Ondrej Lhotak + */ +public abstract class VarNode extends ValNode { + protected Object variable; + protected Map fields; + + protected boolean interProcTarget = false; + protected boolean interProcSource = false; + + protected VarNode(Object variable, Type t) { + super(t); + if (!(t instanceof ReferenceType) /*|| t instanceof AnySubType*/) { + throw new RuntimeException("Attempt to create VarNode of type " + t); + } + this.variable = variable; + } + + public Context context() { + return null; + } + + /** Returns all field ref nodes having this node as their base. */ + public Collection getAllFieldRefs() { + if (fields == null) { + return Collections.emptyList(); + } + return fields.values(); + } + + /** + * Returns the field ref node having this node as its base, and field as its field; null if + * nonexistent. + */ + public FieldRefNode dot(SparkField field) { + return fields == null ? null : fields.get(field); + } + + /** Returns the underlying variable that this node represents. */ + public Object getVariable() { + return variable; + } + + /** + * Designates this node as the potential target of a interprocedural assignment edge which may be + * added during on-the-fly call graph updating. + */ + public void setInterProcTarget() { + interProcTarget = true; + } + + /** + * Returns true if this node is the potential target of a interprocedural assignment edge which + * may be added during on-the-fly call graph updating. + */ + public boolean isInterProcTarget() { + return interProcTarget; + } + + /** + * Designates this node as the potential source of a interprocedural assignment edge which may be + * added during on-the-fly call graph updating. + */ + public void setInterProcSource() { + interProcSource = true; + } + + /** + * Returns true if this node is the potential source of a interprocedural assignment edge which + * may be added during on-the-fly call graph updating. + */ + public boolean isInterProcSource() { + return interProcSource; + } + + public abstract VarNode base(); + + public abstract SootMethod getMethod(); + + /** Registers a frn as having this node as its base. */ + void addField(FieldRefNode frn, SparkField field) { + if (fields == null) { + synchronized (this) { + if (fields == null) { + fields = DataFactory.createMap(); + } + } + } + fields.put(field, frn); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/pag/VirtualCallSite.java b/sootup.qilin/src/main/java/qilin/core/pag/VirtualCallSite.java new file mode 100644 index 00000000000..42696812805 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/pag/VirtualCallSite.java @@ -0,0 +1,91 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.pag; + +import java.util.Objects; +import qilin.core.builder.callgraph.Kind; +import sootup.core.jimple.common.expr.AbstractInstanceInvokeExpr; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.signatures.MethodSubSignature; + +/** + * Holds relevant information about a particular virtual call site. + * + * @author Ondrej Lhotak + */ +public class VirtualCallSite extends CallSite { + private final VarNode recNode; + private final ContextMethod container; + private final AbstractInstanceInvokeExpr iie; + private final MethodSubSignature subSig; + private final Kind kind; + + public VirtualCallSite( + VarNode recNode, + Stmt stmt, + ContextMethod container, + AbstractInstanceInvokeExpr iie, + MethodSubSignature subSig, + Kind kind) { + super(stmt); + this.recNode = recNode; + this.container = container; + this.iie = iie; + this.subSig = subSig; + this.kind = kind; + } + + public VarNode recNode() { + return recNode; + } + + public ContextMethod container() { + return container; + } + + public AbstractInstanceInvokeExpr iie() { + return iie; + } + + public MethodSubSignature subSig() { + return subSig; + } + + public Kind kind() { + return kind; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + VirtualCallSite that = (VirtualCallSite) o; + return recNode.equals(that.recNode) + && container.equals(that.container) + && iie.equals(that.iie) + && subSig.equals(that.subSig) + && kind.equals(that.kind); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), recNode, container, iie, subSig, kind); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/pag/package-info.java b/sootup.qilin/src/main/java/qilin/core/pag/package-info.java new file mode 100644 index 00000000000..11dd57abbe9 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/pag/package-info.java @@ -0,0 +1,7 @@ +package qilin.core.pag; + +/* + * Most of the class files in this package can be seen in the Soot/jimple/spark/pag. + * We have changed them a lot but (at least) their names are kept the same here. + * We hope our framework could be merged into soot and replace the original Spark one day. + * */ diff --git a/sootup.qilin/src/main/java/qilin/core/reflection/NopReflectionModel.java b/sootup.qilin/src/main/java/qilin/core/reflection/NopReflectionModel.java new file mode 100644 index 00000000000..59be216aba2 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/reflection/NopReflectionModel.java @@ -0,0 +1,80 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.reflection; + +import java.util.Collection; +import java.util.Collections; +import qilin.core.PTAScene; +import sootup.core.jimple.common.stmt.Stmt; + +/* + * This is an empty reflection model which does nothing for reflection statements. + * */ + +public class NopReflectionModel extends ReflectionModel { + + public NopReflectionModel(PTAScene scene) { + super(scene); + } + + @Override + Collection transformClassForName(Stmt s) { + return Collections.emptySet(); + } + + @Override + Collection transformClassNewInstance(Stmt s) { + return Collections.emptySet(); + } + + @Override + Collection transformContructorNewInstance(Stmt s) { + return Collections.emptySet(); + } + + @Override + Collection transformMethodInvoke(Stmt s) { + return Collections.emptySet(); + } + + @Override + Collection transformFieldSet(Stmt s) { + return Collections.emptySet(); + } + + @Override + Collection transformFieldGet(Stmt s) { + return Collections.emptySet(); + } + + @Override + Collection transformArrayNewInstance(Stmt s) { + return Collections.emptySet(); + } + + @Override + Collection transformArrayGet(Stmt s) { + return Collections.emptySet(); + } + + @Override + Collection transformArraySet(Stmt s) { + return Collections.emptySet(); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/reflection/ReflectionKind.java b/sootup.qilin/src/main/java/qilin/core/reflection/ReflectionKind.java new file mode 100644 index 00000000000..581197869b8 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/reflection/ReflectionKind.java @@ -0,0 +1,58 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.reflection; + +public enum ReflectionKind { + ClassForName, + ClassNewInstance, + ConstructorNewInstance, + MethodInvoke, + FieldSet, + FieldGet, + MethodGet, + ArrayNewInstance, + ArrayGet, + ArraySet, + FieldGetName; + + public static ReflectionKind parse(String kindStr) { + switch (kindStr) { + case "Class.forName": + return ClassForName; + case "Class.newInstance": + return ClassNewInstance; + case "Constructor.newInstance": + return ConstructorNewInstance; + case "Method.invoke": + return MethodInvoke; + case "Method.getName": + return MethodGet; + case "Field.set*": + return FieldSet; + case "Field.get*": + return FieldGet; + case "Field.getName": + return FieldGetName; + case "Array.newInstance": + return ArrayNewInstance; + default: + return null; + } + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/reflection/ReflectionModel.java b/sootup.qilin/src/main/java/qilin/core/reflection/ReflectionModel.java new file mode 100644 index 00000000000..a4f2faa108a --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/reflection/ReflectionModel.java @@ -0,0 +1,156 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.reflection; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import qilin.core.PTAScene; +import qilin.util.DataFactory; +import qilin.util.PTAUtils; +import sootup.core.graph.MutableStmtGraph; +import sootup.core.jimple.common.expr.AbstractInvokeExpr; +import sootup.core.jimple.common.stmt.FallsThroughStmt; +import sootup.core.jimple.common.stmt.JAssignStmt; +import sootup.core.jimple.common.stmt.JInvokeStmt; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.Body; +import sootup.core.model.SootMethod; + +public abstract class ReflectionModel { + protected final PTAScene ptaScene; + protected final String sigForName = + ""; + protected final String sigForName2 = + ""; + protected final String sigClassNewInstance = ""; + protected final String sigConstructorNewInstance = + ""; + protected final String sigMethodInvoke = + ""; + protected final String sigFieldSet = + ""; + protected final String sigFieldGet = + ""; + protected final String sigArrayNewInstance = + ""; + protected final String sigArrayGet = + ""; + protected final String sigArraySet = + ""; + protected final String sigReifiedField = + ""; + protected final String sigReifiedDeclaredField = + ""; + protected final String sigReifiedFieldArray = + ""; + protected final String sigReifiedDeclaredFieldArray = + ""; + protected final String sigReifiedMethod = + ""; + protected final String sigReifiedDeclaredMethod = + ""; + protected final String sigReifiedMethodArray = + ""; + protected final String sigReifiedDeclaredMethodArray = + ""; + + protected ReflectionModel(PTAScene ptaScene) { + this.ptaScene = ptaScene; + } + + private Collection transform(Stmt s) { + AbstractInvokeExpr ie = s.getInvokeExpr(); + switch (ie.getMethodSignature().toString()) { + case sigForName: + case sigForName2: + return transformClassForName(s); + case sigClassNewInstance: + return transformClassNewInstance(s); + case sigConstructorNewInstance: + return transformContructorNewInstance(s); + case sigMethodInvoke: + return transformMethodInvoke(s); + case sigFieldSet: + return transformFieldSet(s); + case sigFieldGet: + return transformFieldGet(s); + case sigArrayNewInstance: + return transformArrayNewInstance(s); + case sigArrayGet: + return transformArrayGet(s); + case sigArraySet: + return transformArraySet(s); + default: + return Collections.emptySet(); + } + } + + /** replace reflection call with appropriate statements */ + public void buildReflection(SootMethod m) { + if (!ptaScene.reflectionBuilt.add(m)) { + return; + } + Map> newUnits = DataFactory.createMap(); + Body body = PTAUtils.getMethodBody(m); + List units = body.getStmts(); + for (final Stmt u : units) { + if (u.containsInvokeExpr()) { + newUnits.put(u, transform(u)); + } + } + Body.BodyBuilder builder = Body.builder(body, Collections.emptySet()); + final MutableStmtGraph stmtGraph = builder.getStmtGraph(); + for (Stmt unit : newUnits.keySet()) { + for (Stmt succ : newUnits.get(unit)) { + if (succ instanceof JAssignStmt) { + JAssignStmt assign = (JAssignStmt) succ; + stmtGraph.insertBefore(unit, assign); + } else if (succ instanceof JInvokeStmt) { + JInvokeStmt invoke = (JInvokeStmt) succ; + stmtGraph.insertBefore(unit, invoke); + } else { + System.out.println("unit:" + unit); + System.out.println("succ:" + succ.getClass()); + stmtGraph.putEdge((FallsThroughStmt) unit, succ); + } + } + } + PTAUtils.updateMethodBody(m, builder.build()); + } + + abstract Collection transformClassForName(Stmt s); + + abstract Collection transformClassNewInstance(Stmt s); + + abstract Collection transformContructorNewInstance(Stmt s); + + abstract Collection transformMethodInvoke(Stmt s); + + abstract Collection transformFieldSet(Stmt s); + + abstract Collection transformFieldGet(Stmt s); + + abstract Collection transformArrayNewInstance(Stmt s); + + abstract Collection transformArrayGet(Stmt s); + + abstract Collection transformArraySet(Stmt s); +} diff --git a/sootup.qilin/src/main/java/qilin/core/reflection/TamiflexModel.java b/sootup.qilin/src/main/java/qilin/core/reflection/TamiflexModel.java new file mode 100644 index 00000000000..917a338531a --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/reflection/TamiflexModel.java @@ -0,0 +1,484 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.reflection; + +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.*; +import qilin.CoreConfig; +import qilin.core.PTAScene; +import qilin.util.DataFactory; +import qilin.util.PTAUtils; +import sootup.core.jimple.Jimple; +import sootup.core.jimple.basic.*; +import sootup.core.jimple.common.constant.ClassConstant; +import sootup.core.jimple.common.constant.IntConstant; +import sootup.core.jimple.common.constant.NullConstant; +import sootup.core.jimple.common.expr.AbstractInvokeExpr; +import sootup.core.jimple.common.expr.JNewArrayExpr; +import sootup.core.jimple.common.expr.JNewExpr; +import sootup.core.jimple.common.expr.JSpecialInvokeExpr; +import sootup.core.jimple.common.expr.JStaticInvokeExpr; +import sootup.core.jimple.common.expr.JVirtualInvokeExpr; +import sootup.core.jimple.common.ref.JArrayRef; +import sootup.core.jimple.common.ref.JFieldRef; +import sootup.core.jimple.common.stmt.JAssignStmt; +import sootup.core.jimple.common.stmt.JInvokeStmt; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.Body; +import sootup.core.model.SootClass; +import sootup.core.model.SootField; +import sootup.core.model.SootMethod; +import sootup.core.signatures.FieldSignature; +import sootup.core.signatures.MethodSubSignature; +import sootup.core.types.ArrayType; +import sootup.core.types.ReferenceType; +import sootup.java.core.JavaIdentifierFactory; +import sootup.java.core.language.JavaJimple; + +/** + * This reflection model handles reflection according to the dynamic traces recorded through + * Tamiflex. + */ +public class TamiflexModel extends ReflectionModel { + protected Map>> reflectionMap; + + public TamiflexModel(PTAScene ptaScene) { + super(ptaScene); + this.reflectionMap = DataFactory.createMap(); + parseTamiflexLog(CoreConfig.v().getAppConfig().REFLECTION_LOG, false); + } + + @Override + Collection transformClassForName(Stmt s) { + // + // + Collection ret = DataFactory.createSet(); + Map> classForNames = + reflectionMap.getOrDefault(ReflectionKind.ClassForName, Collections.emptyMap()); + if (classForNames.containsKey(s)) { + Collection fornames = classForNames.get(s); + for (String clazz : fornames) { + // !TODO potential bug + ClassConstant cc = JavaJimple.getInstance().newClassConstant(dot2slashStyle(clazz)); + if (s instanceof JAssignStmt) { + LValue lvalue = ((JAssignStmt) s).getLeftOp(); + ret.add(new JAssignStmt(lvalue, cc, StmtPositionInfo.getNoStmtPositionInfo())); + } + } + } + return ret; + } + + public static String dot2slashStyle(String clazz) { + String x = clazz.replace('.', '/'); + return "L" + x + ";"; + } + + @Override + protected Collection transformClassNewInstance(Stmt s) { + // + if (!(s instanceof JAssignStmt)) { + return Collections.emptySet(); + } + LValue lvalue = ((JAssignStmt) s).getLeftOp(); + Collection ret = DataFactory.createSet(); + Map> classNewInstances = + reflectionMap.getOrDefault(ReflectionKind.ClassNewInstance, Collections.emptyMap()); + if (classNewInstances.containsKey(s)) { + Collection classNames = classNewInstances.get(s); + for (String clsName : classNames) { + SootClass cls = ptaScene.getSootClass(clsName); + MethodSubSignature initSubSig = + JavaIdentifierFactory.getInstance().parseMethodSubSignature("void ()"); + Optional omthd = cls.getMethod(initSubSig); + if (omthd.isPresent()) { + JNewExpr newExpr = new JNewExpr(cls.getType()); + ret.add(new JAssignStmt(lvalue, newExpr, StmtPositionInfo.getNoStmtPositionInfo())); + SootMethod constructor = omthd.get(); + ret.add( + new JInvokeStmt( + new JSpecialInvokeExpr( + (Local) lvalue, constructor.getSignature(), Collections.emptyList()), + StmtPositionInfo.getNoStmtPositionInfo())); + } + } + } + return ret; + } + + @Override + protected Collection transformContructorNewInstance(Stmt s) { + // + if (!(s instanceof JAssignStmt)) { + return Collections.emptySet(); + } + LValue lvalue = ((JAssignStmt) s).getLeftOp(); + Collection ret = DataFactory.createSet(); + Map> constructorNewInstances = + reflectionMap.getOrDefault(ReflectionKind.ConstructorNewInstance, Collections.emptyMap()); + if (constructorNewInstances.containsKey(s)) { + Collection constructorSignatures = constructorNewInstances.get(s); + AbstractInvokeExpr iie = s.getInvokeExpr(); + Value args = iie.getArg(0); + JArrayRef arrayRef = + JavaJimple.getInstance().newArrayRef((Local) args, IntConstant.getInstance(0)); + Local arg = + Jimple.newLocal("intermediate/" + arrayRef, PTAUtils.getClassType("java.lang.Object")); + ret.add(new JAssignStmt(arg, arrayRef, StmtPositionInfo.getNoStmtPositionInfo())); + for (String constructorSignature : constructorSignatures) { + SootMethod constructor = ptaScene.getMethod(constructorSignature); + JNewExpr newExpr = new JNewExpr(constructor.getDeclaringClassType()); + ret.add(new JAssignStmt(lvalue, newExpr, StmtPositionInfo.getNoStmtPositionInfo())); + int argCount = constructor.getParameterCount(); + List mArgs = new ArrayList<>(argCount); + for (int i = 0; i < argCount; i++) { + mArgs.add(arg); + } + ret.add( + new JInvokeStmt( + new JSpecialInvokeExpr((Local) lvalue, constructor.getSignature(), mArgs), + StmtPositionInfo.getNoStmtPositionInfo())); + } + } + return ret; + } + + @Override + protected Collection transformMethodInvoke(Stmt s) { + // + Collection ret = DataFactory.createSet(); + Map> methodInvokes = + reflectionMap.getOrDefault(ReflectionKind.MethodInvoke, Collections.emptyMap()); + if (methodInvokes.containsKey(s)) { + Collection methodSignatures = methodInvokes.get(s); + AbstractInvokeExpr iie = s.getInvokeExpr(); + Value base = iie.getArg(0); + Value args = iie.getArg(1); + Local arg = null; + if (args.getType() instanceof ArrayType) { + JArrayRef arrayRef = + JavaJimple.getInstance().newArrayRef((Local) args, IntConstant.getInstance(0)); + arg = + Jimple.newLocal("intermediate/" + arrayRef, PTAUtils.getClassType("java.lang.Object")); + ret.add(new JAssignStmt(arg, arrayRef, StmtPositionInfo.getNoStmtPositionInfo())); + } + + for (String methodSignature : methodSignatures) { + SootMethod method = ptaScene.getMethod(methodSignature); + int argCount = method.getParameterCount(); + List mArgs = new ArrayList<>(argCount); + for (int i = 0; i < argCount; i++) { + if (arg != null) { + mArgs.add(arg); + } else { + mArgs.add(NullConstant.getInstance()); + } + } + AbstractInvokeExpr ie; + if (method.isStatic()) { + assert base instanceof NullConstant; + ie = new JStaticInvokeExpr(method.getSignature(), mArgs); + } else { + assert !(base instanceof NullConstant); + ie = new JVirtualInvokeExpr((Local) base, method.getSignature(), mArgs); + } + if (s instanceof JAssignStmt) { + LValue lvalue = ((JAssignStmt) s).getLeftOp(); + ret.add(new JAssignStmt(lvalue, ie, StmtPositionInfo.getNoStmtPositionInfo())); + } else { + ret.add(new JInvokeStmt(ie, StmtPositionInfo.getNoStmtPositionInfo())); + } + } + } + return ret; + } + + @Override + protected Collection transformFieldSet(Stmt s) { + // + Collection ret = DataFactory.createSet(); + Map> fieldSets = + reflectionMap.getOrDefault(ReflectionKind.FieldSet, Collections.emptyMap()); + if (fieldSets.containsKey(s)) { + Collection fieldSignatures = fieldSets.get(s); + AbstractInvokeExpr iie = s.getInvokeExpr(); + Value base = iie.getArg(0); + Value rValue = iie.getArg(1); + for (String fieldSignature : fieldSignatures) { + FieldSignature fieldSig = + JavaIdentifierFactory.getInstance().parseFieldSignature(fieldSignature); + SootField field = ptaScene.getView().getField(fieldSig).get(); + JFieldRef fieldRef; + if (field.isStatic()) { + assert base instanceof NullConstant; + fieldRef = Jimple.newStaticFieldRef(field.getSignature()); + } else { + assert !(base instanceof NullConstant); + fieldRef = Jimple.newInstanceFieldRef((Local) base, field.getSignature()); + } + Stmt stmt = new JAssignStmt(fieldRef, rValue, StmtPositionInfo.getNoStmtPositionInfo()); + ret.add(stmt); + } + } + return ret; + } + + @Override + protected Collection transformFieldGet(Stmt s) { + // + Collection ret = DataFactory.createSet(); + Map> fieldGets = + reflectionMap.getOrDefault(ReflectionKind.FieldGet, Collections.emptyMap()); + if (fieldGets.containsKey(s) && s instanceof JAssignStmt) { + Collection fieldSignatures = fieldGets.get(s); + LValue lvalue = ((JAssignStmt) s).getLeftOp(); + AbstractInvokeExpr iie = s.getInvokeExpr(); + Value base = iie.getArg(0); + for (String fieldSignature : fieldSignatures) { + FieldSignature fieldSig = + JavaIdentifierFactory.getInstance().parseFieldSignature(fieldSignature); + SootField field = ptaScene.getView().getField(fieldSig).get(); + JFieldRef fieldRef; + if (field.isStatic()) { + assert base instanceof NullConstant; + fieldRef = Jimple.newStaticFieldRef(field.getSignature()); + } else { + assert !(base instanceof NullConstant); + fieldRef = Jimple.newInstanceFieldRef((Local) base, field.getSignature()); + } + if (fieldRef.getType() instanceof ReferenceType) { + Stmt stmt = new JAssignStmt(lvalue, fieldRef, StmtPositionInfo.getNoStmtPositionInfo()); + ret.add(stmt); + } + } + } + return ret; + } + + @Override + protected Collection transformArrayNewInstance(Stmt s) { + // + Collection ret = DataFactory.createSet(); + Map> mappedToArrayTypes = + reflectionMap.getOrDefault(ReflectionKind.ArrayNewInstance, Collections.emptyMap()); + Collection arrayTypes = mappedToArrayTypes.getOrDefault(s, Collections.emptySet()); + for (String arrayType : arrayTypes) { + ArrayType at = (ArrayType) JavaIdentifierFactory.getInstance().getType(arrayType); + JNewArrayExpr newExpr = + JavaJimple.getInstance().newNewArrayExpr(at.getElementType(), IntConstant.getInstance(1)); + if (s instanceof JAssignStmt) { + LValue lvalue = ((JAssignStmt) s).getLeftOp(); + ret.add(new JAssignStmt(lvalue, newExpr, StmtPositionInfo.getNoStmtPositionInfo())); + } + } + return ret; + } + + @Override + Collection transformArrayGet(Stmt s) { + Collection ret = DataFactory.createSet(); + AbstractInvokeExpr iie = s.getInvokeExpr(); + Value base = iie.getArg(0); + if (s instanceof JAssignStmt) { + LValue lvalue = ((JAssignStmt) s).getLeftOp(); + Value arrayRef = null; + if (base.getType() instanceof ArrayType) { + arrayRef = JavaJimple.getInstance().newArrayRef((Local) base, IntConstant.getInstance(0)); + } else if (base.getType() == PTAUtils.getClassType("java.lang.Object")) { + Local local = + Jimple.newLocal( + "intermediate/" + base, + new ArrayType(PTAUtils.getClassType("java.lang.Object"), 1)); + ret.add(new JAssignStmt(local, base, StmtPositionInfo.getNoStmtPositionInfo())); + arrayRef = JavaJimple.getInstance().newArrayRef(local, IntConstant.getInstance(0)); + } + if (arrayRef != null) { + ret.add(new JAssignStmt(lvalue, arrayRef, StmtPositionInfo.getNoStmtPositionInfo())); + } + } + return ret; + } + + @Override + Collection transformArraySet(Stmt s) { + Collection ret = DataFactory.createSet(); + AbstractInvokeExpr iie = s.getInvokeExpr(); + Value base = iie.getArg(0); + if (base.getType() instanceof ArrayType) { + Value from = iie.getArg(2); + JArrayRef arrayRef = + JavaJimple.getInstance().newArrayRef((Local) base, IntConstant.getInstance(0)); + ret.add(new JAssignStmt(arrayRef, from, StmtPositionInfo.getNoStmtPositionInfo())); + } + return ret; + } + + /* + * parse reflection log generated by Tamiflex. + * */ + private void parseTamiflexLog(String logFile, boolean verbose) { + try { + BufferedReader reader = + new BufferedReader(new InputStreamReader(new FileInputStream(logFile))); + String line; + while ((line = reader.readLine()) != null) { + String[] portions = line.split(";", -1); + if (portions.length < 4) { + if (verbose) { + System.out.println("Warning: illegal tamiflex log: " + line); + } + continue; + } + ReflectionKind kind = ReflectionKind.parse(portions[0]); + String mappedTarget = portions[1]; + String inClzDotMthdStr = portions[2]; + int lineNumber = portions[3].length() == 0 ? -1 : Integer.parseInt(portions[3]); + // sanity check. + if (kind == null) { + if (verbose) { + System.out.println("Warning: illegal tamiflex reflection kind: " + portions[0]); + } + continue; + } + switch (kind) { + case ClassForName: + break; + case ClassNewInstance: + if (!ptaScene.containsClass(mappedTarget)) { + if (verbose) { + System.out.println("Warning: Unknown mapped class for signature: " + mappedTarget); + } + continue; + } + break; + case ConstructorNewInstance: + case MethodInvoke: + if (!ptaScene.containsMethod(mappedTarget)) { + if (verbose) { + System.out.println("Warning: Unknown mapped method for signature: " + mappedTarget); + } + continue; + } + break; + case FieldSet: + case FieldGet: + if (!ptaScene.containsField(mappedTarget)) { + if (verbose) { + System.out.println("Warning: Unknown mapped field for signature: " + mappedTarget); + } + continue; + } + break; + case ArrayNewInstance: + break; + default: + if (verbose) { + System.out.println("Warning: Unsupported reflection kind: " + kind); + } + break; + } + Collection possibleSourceStmts = inferSourceStmt(inClzDotMthdStr, kind, lineNumber); + for (Stmt stmt : possibleSourceStmts) { + reflectionMap + .computeIfAbsent(kind, m -> DataFactory.createMap()) + .computeIfAbsent(stmt, k -> DataFactory.createSet()) + .add(mappedTarget); + } + } + reader.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private Collection inferSourceMethod(String inClzDotMthd) { + String inClassStr = inClzDotMthd.substring(0, inClzDotMthd.lastIndexOf(".")); + String inMethodStr = inClzDotMthd.substring(inClzDotMthd.lastIndexOf(".") + 1); + if (!ptaScene.containsClass(inClassStr)) { + System.out.println("Warning: unknown class \"" + inClassStr + "\" is referenced."); + return Collections.emptySet(); + } + SootClass sootClass = ptaScene.getSootClass(inClassStr); + Set ret = DataFactory.createSet(); + Set declMethods = sootClass.getMethods(); + for (SootMethod m : declMethods) { + if (m.isConcrete() && m.getName().equals(inMethodStr)) { + ret.add(m); + } + } + return ret; + } + + private Collection inferSourceStmt( + String inClzDotMthd, ReflectionKind kind, int lineNumber) { + Set ret = DataFactory.createSet(); + Set potential = DataFactory.createSet(); + Collection sourceMethods = inferSourceMethod(inClzDotMthd); + for (SootMethod sm : sourceMethods) { + Body body = PTAUtils.getMethodBody(sm); + for (Stmt stmt : body.getStmts()) { + if (stmt.containsInvokeExpr()) { + String methodSig = stmt.getInvokeExpr().getMethodSignature().toString(); + if (matchReflectionKind(kind, methodSig)) { + potential.add(stmt); + } + } + } + } + for (Stmt stmt : potential) { + int firstLine = stmt.getPositionInfo().getStmtPosition().getFirstLine(); + int lastLine = stmt.getPositionInfo().getStmtPosition().getLastLine(); + // !TODO potential bug here + if (lineNumber < 0 || firstLine <= lineNumber && lineNumber <= lastLine) { + ret.add(stmt); + } + } + if (ret.size() == 0 && potential.size() > 0) { + System.out.print("Warning: Mismatch between statement and reflection log entry - "); + System.out.println(kind + ";" + inClzDotMthd + ";" + lineNumber + ";"); + return potential; + } else { + return ret; + } + } + + private boolean matchReflectionKind(ReflectionKind kind, String methodSig) { + switch (kind) { + case ClassForName: + return methodSig.equals(sigForName) || methodSig.equals(sigForName2); + case ClassNewInstance: + return methodSig.equals(sigClassNewInstance); + case ConstructorNewInstance: + return methodSig.equals(sigConstructorNewInstance); + case MethodInvoke: + return methodSig.equals(sigMethodInvoke); + case FieldSet: + return methodSig.equals(sigFieldSet); + case FieldGet: + return methodSig.equals(sigFieldGet); + case ArrayNewInstance: + return methodSig.equals(sigArrayNewInstance); + default: + return false; + } + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/sets/DoublePointsToSet.java b/sootup.qilin/src/main/java/qilin/core/sets/DoublePointsToSet.java new file mode 100644 index 00000000000..303d4f8f58c --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/sets/DoublePointsToSet.java @@ -0,0 +1,137 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.sets; + +import java.util.Iterator; + +/** + * Implementation of points-to set that holds two sets: one for new elements that have not yet been + * propagated, and the other for elements that have already been propagated. + * + * @author Ondrej Lhotak + */ +public class DoublePointsToSet extends PointsToSetInternal { + protected HybridPointsToSet newSet; + protected HybridPointsToSet oldSet; + + public DoublePointsToSet() { + newSet = new HybridPointsToSet(); + oldSet = new HybridPointsToSet(); + } + + /** Returns true if this set contains no run-time objects. */ + @Override + public boolean isEmpty() { + return oldSet.isEmpty() && newSet.isEmpty(); + } + + /** Returns true if this set shares some objects with other. */ + public boolean hasNonEmptyIntersection(PointsToSetInternal other) { + return oldSet.hasNonEmptyIntersection(other) || newSet.hasNonEmptyIntersection(other); + } + + @Override + public int size() { + return oldSet.size() + newSet.size(); + } + + private class DoublePTSIterator implements Iterator { + private final Iterator oldIt = oldSet.iterator(); + private final Iterator newIt = newSet.iterator(); + + @Override + public boolean hasNext() { + return oldIt.hasNext() || newIt.hasNext(); + } + + @Override + public Integer next() { + if (oldIt.hasNext()) { + return oldIt.next(); + } else { + return newIt.next(); + } + } + } + + public Iterator iterator() { + return new DoublePTSIterator(); + } + + /* + * Empty this set. + * */ + @Override + public void clear() { + oldSet.clear(); + newSet.clear(); + } + + /** Adds contents of other into this set, returns true if this set changed. */ + public boolean addAll(PointsToSetInternal other, PointsToSetInternal exclude) { + if (exclude != null) { + throw new RuntimeException("exclude set must be null."); + } + return newSet.addAll(other, oldSet); + } + + /** Calls v's visit method on all nodes in this set. */ + @Override + public boolean forall(P2SetVisitor v) { + oldSet.forall(v); + newSet.forall(v); + return v.getReturnValue(); + } + + /** Adds n to this set, returns true if idx was not already in this set. */ + public boolean add(int idx) { + if (oldSet.contains(idx)) { + return false; + } + return newSet.add(idx); + } + + /** Returns set of nodes already present before last call to flushNew. */ + public HybridPointsToSet getOldSet() { + return oldSet; + } + + /** Returns set of newly-added nodes since last call to flushNew. */ + public HybridPointsToSet getNewSet() { + return newSet; + } + + public HybridPointsToSet getNewSetCopy() { + HybridPointsToSet newCopy = new HybridPointsToSet(); + newCopy.addAll(newSet, null); + return newCopy; + } + + /** Sets all newly-added nodes to old nodes. */ + public void flushNew() { + oldSet.addAll(newSet, null); + newSet = new HybridPointsToSet(); + } + + /** Returns true iff the set contains idx. */ + @Override + public boolean contains(int idx) { + return oldSet.contains(idx) || newSet.contains(idx); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/sets/HybridPointsToSet.java b/sootup.qilin/src/main/java/qilin/core/sets/HybridPointsToSet.java new file mode 100644 index 00000000000..a58f09fb64c --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/sets/HybridPointsToSet.java @@ -0,0 +1,188 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.sets; + +import java.util.Arrays; +import java.util.Iterator; +import qilin.util.BitSetIterator; +import qilin.util.BitVector; + +/** + * Hybrid implementation of points-to set, which uses an explicit array for small sets, and a bit + * vector for large sets. + * + * @author Ondrej Lhotak + */ +public final class HybridPointsToSet extends PointsToSetInternal { + private static HybridPointsToSet emptySet = null; + + public static HybridPointsToSet getEmptySet() { + if (emptySet == null) { + emptySet = new HybridPointsToSet(); + } + return emptySet; + } + + private final int[] nodeIdxs = new int[16]; + private BitVector bits = null; + private int size = 0; + + private boolean empty = true; + + /** Returns true if this set contains no run-time objects. */ + @Override + public boolean isEmpty() { + return empty; + } + + @Override + public void clear() { + Arrays.fill(nodeIdxs, 0); + bits = null; + empty = true; + size = 0; + } + + private boolean nativeAddAll(HybridPointsToSet other, PointsToSetInternal exclude) { + boolean ret = false; + for (Iterator it = other.iterator(); it.hasNext(); ) { + int idx = it.next(); + if (exclude == null || !exclude.contains(idx)) { + ret |= add(idx); + } + } + return ret; + } + + /** Adds contents of other into this set, returns true if this set changed. */ + public boolean addAll(final PointsToSetInternal other, final PointsToSetInternal exclude) { + if (other == null) { + return false; + } + if (other instanceof DoublePointsToSet) { + DoublePointsToSet dpts = (DoublePointsToSet) other; + return nativeAddAll(dpts.getNewSet(), exclude) | nativeAddAll(dpts.getOldSet(), exclude); + } + return nativeAddAll((HybridPointsToSet) other, exclude); + } + + private class HybridPTSIterator implements Iterator { + private BitSetIterator it; + private int idx; + + public HybridPTSIterator() { + if (bits == null) { + idx = 0; + } else { + it = bits.iterator(); + } + } + + @Override + public boolean hasNext() { + if (bits == null) { + return idx < nodeIdxs.length && nodeIdxs[idx] != 0; + } else { + return it.hasNext(); + } + } + + @Override + public Integer next() { + if (bits == null) { + return nodeIdxs[idx++]; + } else { + return it.next(); + } + } + } + + @Override + public Iterator iterator() { + return new HybridPTSIterator(); + } + + @Override + public int size() { + return size; + } + + /** Calls v's visit method on all nodes in this set. */ + public boolean forall(P2SetVisitor v) { + if (bits == null) { + for (int nodeIdx : nodeIdxs) { + if (nodeIdx == 0) { + return v.getReturnValue(); + } + v.visit(nodeIdx); + } + } else { + for (BitSetIterator it = bits.iterator(); it.hasNext(); ) { + v.visit(it.next()); + } + } + return v.getReturnValue(); + } + + /** Returns true iff the set contains node idx. */ + public boolean contains(int idx) { + if (bits == null) { + for (int nodeIdx : nodeIdxs) { + if (idx == nodeIdx) { + return true; + } + if (nodeIdx == 0) { + break; + } + } + return false; + } else { + return bits.get(idx); + } + } + + /** Adds idx to this set, returns true if idx was not already in this set. */ + public boolean add(int idx) { + if (bits == null) { + for (int i = 0; i < nodeIdxs.length; i++) { + if (nodeIdxs[i] == 0) { + empty = false; + nodeIdxs[i] = idx; + ++size; + return true; + } else if (nodeIdxs[i] == idx) { + return false; + } + } + // convert to Bits + bits = new BitVector(); + for (int nodeIdx : nodeIdxs) { + if (nodeIdx != 0) { + bits.set(nodeIdx); + } + } + } + boolean ret = bits.set(idx); + if (ret) { + ++size; + empty = false; + } + return ret; + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/sets/P2SetVisitor.java b/sootup.qilin/src/main/java/qilin/core/sets/P2SetVisitor.java new file mode 100644 index 00000000000..46b3fff8f45 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/sets/P2SetVisitor.java @@ -0,0 +1,47 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.sets; + +import qilin.core.PTA; +import qilin.core.pag.Node; + +/** + * Abstract base class for points-to set visitors used to enumerate points-to sets. + * + * @author Ondrej Lhotak + */ +public abstract class P2SetVisitor { + protected boolean returnValue = false; + protected final PTA pta; + + protected P2SetVisitor(PTA pta) { + this.pta = pta; + } + + protected abstract void visit(Node n); + + public void visit(long idx) { + Node node = pta.getPag().getAllocNodeNumberer().get(idx); + visit(node); + } + + public boolean getReturnValue() { + return returnValue; + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/sets/PointsToSet.java b/sootup.qilin/src/main/java/qilin/core/sets/PointsToSet.java new file mode 100644 index 00000000000..df0ee5b0418 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/sets/PointsToSet.java @@ -0,0 +1,91 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.sets; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; +import qilin.core.pag.AllocNode; +import sootup.core.jimple.common.constant.ClassConstant; +import sootup.core.types.Type; + +/** + * A generic interface to some set of runtime objects computed by a pointer analysis. + * + * @author Ondrej Lhotak + */ +public interface PointsToSet { + /** Returns true if this set contains no run-time objects. */ + boolean isEmpty(); + + /** Returns true iff the set contains n. */ + boolean contains(AllocNode n); + + /** Returns true if this set shares some objects with other. */ + boolean hasNonEmptyIntersection(PointsToSet other); + + /** Set of all possible run-time types of objects in the set. */ + Set possibleTypes(); + + /** + * If this points-to set consists entirely of string constants, returns a set of these constant + * strings. If this point-to set may contain something other than constant strings, returns null. + */ + Set possibleStringConstants(); + + /** + * If this points-to set consists entirely of objects of type java.lang.Class of a known class, + * returns a set of ClassConstant's that are these classes. If this point-to set may contain + * something else, returns null. + */ + Set possibleClassConstants(); + + /** + * Size of objects in this set. + * + * @author Dongjie He + */ + int size(); + + /* + * Empty this set. + * + * @author Dongjie He + * */ + void clear(); + + /** + * Computes a hash code based on the contents of the points-to set. Note that hashCode() is not + * overwritten on purpose. This is because Spark relies on comparison by object identity. + */ + int pointsToSetHashCode(); + + /** + * Returns true if and only if other holds the same alloc nodes as this. Note that + * equals() is not overwritten on purpose. This is because Spark relies on comparison by object + * identity. + */ + boolean pointsToSetEquals(Object other); + + PointsToSet toCIPointsToSet(); + + Collection toCollection(); + + Iterator iterator(); +} diff --git a/sootup.qilin/src/main/java/qilin/core/sets/PointsToSetInternal.java b/sootup.qilin/src/main/java/qilin/core/sets/PointsToSetInternal.java new file mode 100644 index 00000000000..6435587cd86 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/sets/PointsToSetInternal.java @@ -0,0 +1,59 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.sets; + +import java.util.Iterator; + +/** + * Abstract base class for implementations of points-to sets. + * + * @author Ondrej Lhotak + */ +public abstract class PointsToSetInternal { + /** Calls v's visit method on all nodes in this set. */ + public abstract boolean forall(P2SetVisitor v); + + public abstract boolean addAll( + final PointsToSetInternal other, final PointsToSetInternal exclude); + + /** Adds node index idx to this set, returns true if idx was not already in this set. */ + public abstract boolean add(int idx); + + /** Returns true iff the set contains the node number index. */ + public abstract boolean contains(int idx); + + public abstract Iterator iterator(); + + public abstract void clear(); + + public abstract boolean isEmpty(); + + public boolean hasNonEmptyIntersection(final PointsToSetInternal other) { + Iterator it = iterator(); + while (it.hasNext()) { + int idx = it.next(); + if (other.contains(idx)) { + return true; + } + } + return false; + } + + public abstract int size(); +} diff --git a/sootup.qilin/src/main/java/qilin/core/sets/UnmodifiablePointsToSet.java b/sootup.qilin/src/main/java/qilin/core/sets/UnmodifiablePointsToSet.java new file mode 100644 index 00000000000..1c2b520af98 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/sets/UnmodifiablePointsToSet.java @@ -0,0 +1,232 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.sets; + +import java.util.*; +import qilin.core.PTA; +import qilin.core.pag.AllocNode; +import qilin.core.pag.ClassConstantNode; +import qilin.core.pag.Node; +import qilin.core.pag.StringConstantNode; +import sootup.core.jimple.common.constant.ClassConstant; +import sootup.core.model.SootClass; +import sootup.core.types.ClassType; +import sootup.core.types.Type; +import sootup.core.views.View; + +public class UnmodifiablePointsToSet implements PointsToSet { + private final PointsToSetInternal pts; + private final PTA pta; + + public UnmodifiablePointsToSet(PTA pta, PointsToSetInternal pts) { + this.pta = pta; + this.pts = pts; + } + + @Override + public boolean isEmpty() { + return pts.isEmpty(); + } + + @Override + public boolean contains(AllocNode n) { + return pts.contains(n.getNumber()); + } + + @Override + public boolean hasNonEmptyIntersection(PointsToSet other) { + if (other instanceof UnmodifiablePointsToSet) { + UnmodifiablePointsToSet uother = (UnmodifiablePointsToSet) other; + return pta == uother.pta && pts.hasNonEmptyIntersection(uother.pts); + } + return false; + } + + @Override + public Set possibleTypes() { + final Set ret = new HashSet<>(); + pts.forall( + new P2SetVisitor(pta) { + public void visit(Node n) { + Type t = n.getType(); + if (t instanceof ClassType) { + ClassType rt = (ClassType) t; + View view = pta.getView(); + Optional osc = view.getClass(rt); + if (osc.isPresent() && osc.get().isAbstract()) { + return; + } + } + ret.add(t); + } + }); + return ret; + } + + @Override + public Set possibleStringConstants() { + final Set ret = new HashSet<>(); + return pts.forall( + new P2SetVisitor(pta) { + public void visit(Node n) { + if (n instanceof StringConstantNode) { + ret.add(((StringConstantNode) n).getString()); + } else { + returnValue = true; + } + } + }) + ? null + : ret; + } + + @Override + public Set possibleClassConstants() { + final Set ret = new HashSet<>(); + return pts.forall( + new P2SetVisitor(pta) { + public void visit(Node n) { + if (n instanceof ClassConstantNode) { + ret.add(((ClassConstantNode) n).getClassConstant()); + } else { + returnValue = true; + } + } + }) + ? null + : ret; + } + + @Override + public int size() { + return pts.size(); + } + + @Override + public void clear() { + pts.clear(); + } + + @Override + public String toString() { + final StringBuffer ret = new StringBuffer(); + pts.forall( + new P2SetVisitor(pta) { + public void visit(Node n) { + ret.append(n).append(","); + } + }); + return ret.toString(); + } + + @Override + public int pointsToSetHashCode() { + long intValue = 1; + final long PRIME = 31; + for (Iterator it = pts.iterator(); it.hasNext(); ) { + intValue = PRIME * intValue + it.next(); + } + return (int) intValue; + } + + @Override + public boolean pointsToSetEquals(Object other) { + if (this == other) { + return true; + } + if (other instanceof UnmodifiablePointsToSet) { + UnmodifiablePointsToSet otherPts = (UnmodifiablePointsToSet) other; + if (otherPts.pta != pta) { + return false; + } + // both sets are equal if they are supersets of each other + return superSetOf(otherPts.pts, pts) && superSetOf(pts, otherPts.pts); + } + return false; + } + + @Override + public PointsToSet toCIPointsToSet() { + PointsToSetInternal ptoSet = new HybridPointsToSet(); + pts.forall( + new P2SetVisitor(pta) { + @Override + public void visit(Node n) { + AllocNode heap = (AllocNode) n; + ptoSet.add(heap.base().getNumber()); + } + }); + return new UnmodifiablePointsToSet(pta, ptoSet); + } + + @Override + public Collection toCollection() { + Set ret = new HashSet<>(); + for (Iterator it = iterator(); it.hasNext(); ) { + ret.add(it.next()); + } + return ret; + } + + /** + * Returns true if onePts is a (non-strict) superset of otherPts + * . + */ + private boolean superSetOf(PointsToSetInternal onePts, final PointsToSetInternal otherPts) { + Iterator it = onePts.iterator(); + while (it.hasNext()) { + int idx = it.next(); + if (!otherPts.contains(idx)) { + return false; + } + } + return true; + } + + public Iterator iterator() { + return new UnmodifiablePTSIterator(); + } + + private class UnmodifiablePTSIterator implements Iterator { + Iterator it = pts.iterator(); + + @Override + public boolean hasNext() { + return it.hasNext(); + } + + @Override + public AllocNode next() { + int idx = it.next(); + return pta.getPag().getAllocNodeNumberer().get(idx); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + return pointsToSetEquals(o); + } + + @Override + public int hashCode() { + return Objects.hash(pointsToSetHashCode(), pta); + } +} diff --git a/sootup.qilin/src/main/java/qilin/core/sets/package-info.java b/sootup.qilin/src/main/java/qilin/core/sets/package-info.java new file mode 100644 index 00000000000..86cc93076b3 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/sets/package-info.java @@ -0,0 +1,6 @@ +package qilin.core.sets; + +/* + * We reuse the data structures of points-to set which are originally designed for Spark in Soot. + * In future, we plan to design more memory-efficient data structures for replacements. + * */ diff --git a/sootup.qilin/src/main/java/qilin/core/solver/Propagator.java b/sootup.qilin/src/main/java/qilin/core/solver/Propagator.java new file mode 100644 index 00000000000..d15f1993dcc --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/solver/Propagator.java @@ -0,0 +1,30 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.solver; + +/** + * Abstract base class for a propagator that propagates points-to sets along pointer assignment + * graph. + * + * @author Ondrej Lhotak + */ +public abstract class Propagator { + /** Actually does the propagation. */ + public abstract void propagate(); +} diff --git a/sootup.qilin/src/main/java/qilin/core/solver/Solver.java b/sootup.qilin/src/main/java/qilin/core/solver/Solver.java new file mode 100644 index 00000000000..d2008d28c09 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/core/solver/Solver.java @@ -0,0 +1,374 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.core.solver; + +import java.util.*; +import qilin.CoreConfig; +import qilin.core.PTA; +import qilin.core.builder.CallGraphBuilder; +import qilin.core.builder.ExceptionHandler; +import qilin.core.builder.MethodNodeFactory; +import qilin.core.builder.callgraph.Edge; +import qilin.core.builder.callgraph.Kind; +import qilin.core.context.Context; +import qilin.core.pag.*; +import qilin.core.sets.DoublePointsToSet; +import qilin.core.sets.P2SetVisitor; +import qilin.core.sets.PointsToSetInternal; +import qilin.util.PTAUtils; +import qilin.util.queue.ChunkedQueue; +import qilin.util.queue.QueueReader; +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.common.expr.AbstractInstanceInvokeExpr; +import sootup.core.jimple.common.expr.AbstractInvokeExpr; +import sootup.core.jimple.common.expr.JDynamicInvokeExpr; +import sootup.core.jimple.common.stmt.JThrowStmt; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.SootMethod; +import sootup.core.signatures.MethodSignature; +import sootup.core.signatures.MethodSubSignature; +import sootup.core.types.ClassType; +import sootup.core.types.Type; +import sootup.java.core.JavaIdentifierFactory; + +public class Solver extends Propagator { + private final TreeSet valNodeWorkList = new TreeSet<>(); + private final PAG pag; + private final PTA pta; + private final CallGraphBuilder cgb; + private final ExceptionHandler eh; + private final ChunkedQueue throwSiteQueue = new ChunkedQueue<>(); + private final ChunkedQueue virtualCallSiteQueue = new ChunkedQueue<>(); + private final ChunkedQueue edgeQueue = new ChunkedQueue<>(); + + private final ChunkedQueue rmQueue = new ChunkedQueue<>(); + + public Solver(PTA pta) { + this.cgb = pta.getCgb(); + this.cgb.setRMQueue(rmQueue); + this.pag = pta.getPag(); + this.pag.setEdgeQueue(edgeQueue); + this.eh = pta.getExceptionHandler(); + this.pta = pta; + } + + @Override + public void propagate() { + final QueueReader newRMs = rmQueue.reader(); + final QueueReader newPAGEdges = edgeQueue.reader(); + final QueueReader newThrows = throwSiteQueue.reader(); + final QueueReader newCalls = virtualCallSiteQueue.reader(); + cgb.initReachableMethods(); + processStmts(newRMs); + pag.getAlloc().forEach((a, set) -> set.forEach(v -> propagatePTS(v, a))); + while (!valNodeWorkList.isEmpty()) { + ValNode curr = valNodeWorkList.pollFirst(); + // Step 1: Resolving Direct Constraints + assert curr != null; + final DoublePointsToSet pts = curr.getP2Set(); + final PointsToSetInternal newset = pts.getNewSet(); + pag.simpleLookup(curr).forEach(to -> propagatePTS(to, newset)); + + if (curr instanceof VarNode) { + VarNode mSrc = (VarNode) curr; + // Step 1 continues. + Collection throwSites = eh.throwSitesLookUp(mSrc); + for (ExceptionThrowSite site : throwSites) { + eh.exceptionDispatch(newset, site); + } + // Step 2: Resolving Indirect Constraints. + handleStoreAndLoadOnBase(mSrc); + // Step 3: Collecting New Constraints. + Collection sites = cgb.callSitesLookUp(mSrc); + for (VirtualCallSite site : sites) { + cgb.virtualCallDispatch(newset, site); + } + processStmts(newRMs); + } + pts.flushNew(); + // Step 4: Activating New Constraints. + activateConstraints(newCalls, newRMs, newThrows, newPAGEdges); + } + } + + public void processStmts(Iterator newRMs) { + while (newRMs.hasNext()) { + ContextMethod momc = newRMs.next(); + SootMethod method = momc.method(); + if (!PTAUtils.hasBody(method)) { + continue; + } + MethodPAG mpag = pag.getMethodPAG(method); + addToPAG(mpag, momc.context()); + // !FIXME in a context-sensitive pointer analysis, clinits in a method maybe added multiple + // times. + if (CoreConfig.v().getPtaConfig().clinitMode == CoreConfig.ClinitMode.ONFLY) { + // add find in the method to reachableMethods. + Iterator it = mpag.triggeredClinits(); + while (it.hasNext()) { + SootMethod sm = it.next(); + cgb.injectCallEdge( + sm.getDeclaringClassType(), pta.parameterize(sm, pta.emptyContext()), Kind.CLINIT); + } + } + recordCallStmts(momc, mpag.getInvokeStmts()); + recordThrowStmts(momc, mpag.stmt2wrapperedTraps.keySet()); + } + } + + private void recordCallStmts(ContextMethod m, Collection units) { + for (final Stmt s : units) { + if (s.containsInvokeExpr()) { + AbstractInvokeExpr ie = s.getInvokeExpr(); + if (ie instanceof AbstractInstanceInvokeExpr) { + AbstractInstanceInvokeExpr iie = (AbstractInstanceInvokeExpr) ie; + Local receiver = iie.getBase(); + VarNode recNode = cgb.getReceiverVarNode(receiver, m); + MethodSubSignature subSig = iie.getMethodSignature().getSubSignature(); + VirtualCallSite virtualCallSite = + new VirtualCallSite(recNode, s, m, iie, subSig, Edge.ieToKind(iie)); + if (cgb.recordVirtualCallSite(recNode, virtualCallSite)) { + virtualCallSiteQueue.add(virtualCallSite); + } + } else { + MethodSignature tgtSig = ie.getMethodSignature(); + Optional otgt = pta.getView().getMethod(tgtSig); + if (otgt.isPresent()) { + // static invoke or dynamic invoke + VarNode recNode = pag.getMethodPAG(m.method()).nodeFactory().caseThis(); + recNode = (VarNode) pta.parameterize(recNode, m.context()); + if (ie instanceof JDynamicInvokeExpr) { + // !TODO dynamicInvoke is provided in JDK after Java 7. + // currently, PTA does not handle dynamicInvokeExpr. + } else { + cgb.addStaticEdge(m, s, otgt.get(), Edge.ieToKind(ie)); + } + } else { + // + } + } + } + } + } + + private void recordThrowStmts(ContextMethod m, Collection stmts) { + for (final Stmt stmt : stmts) { + SootMethod sm = m.method(); + MethodPAG mpag = pag.getMethodPAG(sm); + MethodNodeFactory nodeFactory = mpag.nodeFactory(); + Node src; + if (stmt.containsInvokeExpr()) { + src = nodeFactory.makeInvokeStmtThrowVarNode(stmt, sm); + } else { + assert stmt instanceof JThrowStmt; + JThrowStmt ts = (JThrowStmt) stmt; + src = nodeFactory.getNode(ts.getOp()); + } + VarNode throwNode = (VarNode) pta.parameterize(src, m.context()); + ExceptionThrowSite throwSite = new ExceptionThrowSite(throwNode, stmt, m); + if (eh.addThrowSite(throwNode, throwSite)) { + throwSiteQueue.add(throwSite); + } + } + } + + private void addToPAG(MethodPAG mpag, Context cxt) { + Set contexts = + pag.getMethod2ContextsMap().computeIfAbsent(mpag, k1 -> new HashSet<>()); + if (!contexts.add(cxt)) { + return; + } + for (QueueReader reader = mpag.getInternalReader().clone(); reader.hasNext(); ) { + Node from = reader.next(); + Node to = reader.next(); + if (from instanceof AllocNode) { + AllocNode heap = (AllocNode) from; + from = pta.heapAbstractor().abstractHeap(heap); + } + if (from instanceof AllocNode && to instanceof GlobalVarNode) { + pag.addGlobalPAGEdge(from, to); + } else { + from = pta.parameterize(from, cxt); + to = pta.parameterize(to, cxt); + if (from instanceof AllocNode) { + handleImplicitCallToFinalizerRegister((AllocNode) from); + } + pag.addEdge(from, to); + } + } + } + + // handle implicit calls to java.lang.ref.Finalizer.register by the JVM. + // please refer to library/finalization.logic in doop. + private void handleImplicitCallToFinalizerRegister(AllocNode heap) { + if (supportFinalize(heap)) { + SootMethod rm = + pta.getScene().getMethod(""); + MethodPAG tgtmpag = pag.getMethodPAG(rm); + MethodNodeFactory tgtnf = tgtmpag.nodeFactory(); + Node parm = tgtnf.caseParm(0); + Context calleeCtx = pta.emptyContext(); + AllocNode baseHeap = heap.base(); + parm = pta.parameterize(parm, calleeCtx); + pag.addEdge(heap, parm); + cgb.injectCallEdge(baseHeap, pta.parameterize(rm, calleeCtx), Kind.STATIC); + } + } + + private boolean supportFinalize(AllocNode heap) { + MethodSubSignature sigFinalize = + JavaIdentifierFactory.getInstance().parseMethodSubSignature("void finalize()"); + Type type = heap.getType(); + if (type instanceof ClassType && type != PTAUtils.getClassType("java.lang.Object")) { + ClassType refType = (ClassType) type; + SootMethod finalizeMethod = cgb.resolveNonSpecial(refType, sigFinalize); + if (finalizeMethod != null + && finalizeMethod.toString().equals("")) { + return false; + } + return finalizeMethod != null; + } + return false; + } + + private void handleStoreAndLoadOnBase(VarNode base) { + for (final FieldRefNode fr : base.getAllFieldRefs()) { + for (final VarNode v : pag.storeInvLookup(fr)) { + handleStoreEdge(base.getP2Set().getNewSet(), fr.getField(), v); + } + for (final VarNode to : pag.loadLookup(fr)) { + handleLoadEdge(base.getP2Set().getNewSet(), fr.getField(), to); + } + } + } + + private void handleStoreEdge(PointsToSetInternal baseHeaps, SparkField field, ValNode from) { + baseHeaps.forall( + new P2SetVisitor(pta) { + public void visit(Node n) { + if (disallowStoreOrLoadOn((AllocNode) n)) { + return; + } + final FieldValNode fvn = pag.makeFieldValNode(field); + final ValNode oDotF = + (ValNode) pta.parameterize(fvn, PTAUtils.plusplusOp((AllocNode) n)); + pag.addEdge(from, oDotF); + } + }); + } + + private void handleLoadEdge(PointsToSetInternal baseHeaps, SparkField field, ValNode to) { + baseHeaps.forall( + new P2SetVisitor(pta) { + public void visit(Node n) { + if (disallowStoreOrLoadOn((AllocNode) n)) { + return; + } + final FieldValNode fvn = pag.makeFieldValNode(field); + final ValNode oDotF = + (ValNode) pta.parameterize(fvn, PTAUtils.plusplusOp((AllocNode) n)); + pag.addEdge(oDotF, to); + } + }); + } + + private void activateConstraints( + QueueReader newCalls, + QueueReader newRMs, + QueueReader newThrows, + QueueReader addedEdges) { + while (newCalls.hasNext()) { + while (newCalls.hasNext()) { + final VirtualCallSite site = newCalls.next(); + final VarNode receiver = site.recNode(); + cgb.virtualCallDispatch(receiver.getP2Set().getOldSet(), site); + } + processStmts(newRMs); // may produce new calls, thus an out-loop is a must. + } + + while (newThrows.hasNext()) { + final ExceptionThrowSite ets = newThrows.next(); + final VarNode throwNode = ets.getThrowNode(); + eh.exceptionDispatch(throwNode.getP2Set().getOldSet(), ets); + } + /* + * there are some actual parameter to formal parameter edges whose source nodes are not in the worklist. + * For this case, we should use the following loop to update the target nodes and insert the + * target nodes into the worklist if nesseary. + * */ + while (addedEdges.hasNext()) { + final Node addedSrc = addedEdges.next(); + final Node addedTgt = addedEdges.next(); + if (addedSrc instanceof VarNode && addedTgt instanceof VarNode + || addedSrc instanceof ContextField + || addedTgt instanceof ContextField) { // x = y; x = o.f; o.f = y; + final ValNode srcv = (ValNode) addedSrc; + final ValNode tgtv = (ValNode) addedTgt; + propagatePTS(tgtv, srcv.getP2Set().getOldSet()); + } else if (addedSrc instanceof FieldRefNode) { + final FieldRefNode srcfrn = (FieldRefNode) addedSrc; // b = a.f + handleLoadEdge( + srcfrn.getBase().getP2Set().getOldSet(), srcfrn.getField(), (ValNode) addedTgt); + } else if (addedTgt instanceof FieldRefNode) { + final FieldRefNode tgtfrn = (FieldRefNode) addedTgt; // a.f = b; + handleStoreEdge( + tgtfrn.getBase().getP2Set().getOldSet(), tgtfrn.getField(), (ValNode) addedSrc); + } else if (addedSrc instanceof AllocNode) { // alloc x = new T; + propagatePTS((VarNode) addedTgt, (AllocNode) addedSrc); + } + } + } + + protected void propagatePTS(final ValNode pointer, PointsToSetInternal other) { + final DoublePointsToSet addTo = pointer.getP2Set(); + P2SetVisitor p2SetVisitor = + new P2SetVisitor(pta) { + @Override + public void visit(Node n) { + if (addWithTypeFiltering(addTo, pointer.getType(), n)) { + returnValue = true; + } + } + }; + other.forall(p2SetVisitor); + if (p2SetVisitor.getReturnValue()) { + valNodeWorkList.add(pointer); + } + } + + protected void propagatePTS(final ValNode pointer, AllocNode heap) { + if (addWithTypeFiltering(pointer.getP2Set(), pointer.getType(), heap)) { + valNodeWorkList.add(pointer); + } + } + + // we do not allow store to and load from constant heap/empty array. + private boolean disallowStoreOrLoadOn(AllocNode heap) { + AllocNode base = heap.base(); + // return base instanceof StringConstantNode || PTAUtils.isEmptyArray(base); + return PTAUtils.isEmptyArray(base); + } + + private boolean addWithTypeFiltering(PointsToSetInternal pts, Type type, Node node) { + if (PTAUtils.castNeverFails(pta.getView(), node.getType(), type)) { + return pts.add(node.getNumber()); + } + return false; + } +} diff --git a/sootup.qilin/src/main/java/qilin/driver/ContextKind.java b/sootup.qilin/src/main/java/qilin/driver/ContextKind.java new file mode 100644 index 00000000000..941e002fa6f --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/driver/ContextKind.java @@ -0,0 +1,69 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.driver; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import qilin.util.Util; + +public enum ContextKind { + INSENS, + CALLSITE, + OBJECT, + TYPE, + HYBOBJ, + HYBTYPE; + static final Map contextKinds = new HashMap<>(); + + static { + Util.add(contextKinds, INSENS, "insensitive", "insens", "ci"); + Util.add(contextKinds, CALLSITE, "callsite", "call", "c"); + Util.add(contextKinds, OBJECT, "object", "obj", "o"); + Util.add(contextKinds, HYBOBJ, "hybobj", "ho", "h"); + Util.add(contextKinds, HYBTYPE, "hybtype", "ht"); + Util.add(contextKinds, TYPE, "type", "t"); + } + + public static Collection contextAliases() { + return contextKinds.keySet(); + } + + public static ContextKind toCtxKind(String name) { + return contextKinds.getOrDefault(name, INSENS); + } + + @Override + public String toString() { + switch (this) { + case CALLSITE: + return "callsite"; + case OBJECT: + return "object"; + case HYBOBJ: + return "hybobj"; + case HYBTYPE: + return "hybtype"; + case TYPE: + return "type"; + default: + return "insensitive"; + } + } +} diff --git a/sootup.qilin/src/main/java/qilin/driver/Main.java b/sootup.qilin/src/main/java/qilin/driver/Main.java new file mode 100644 index 00000000000..dea81bf8348 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/driver/Main.java @@ -0,0 +1,66 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.driver; + +import java.lang.management.ManagementFactory; +import qilin.core.PTA; +import qilin.pta.PTAConfig; +import qilin.util.MemoryWatcher; +import qilin.util.PTAUtils; +import qilin.util.Stopwatch; +import sootup.core.views.View; + +public class Main { + + public static PTA run(String[] args) { + new PTAOption().parseCommandLine(args); + PTAConfig.ApplicationConfiguration appConfig = PTAConfig.v().getAppConfig(); + if (appConfig.MAIN_CLASS == null) { + appConfig.MAIN_CLASS = PTAUtils.findMainFromMetaInfo(appConfig.APP_PATH); + } + View view = PTAUtils.createView(); + PTAPattern ptaPattern = PTAConfig.v().getPtaConfig().ptaPattern; + PTA pta = PTAFactory.createPTA(ptaPattern, view, appConfig.MAIN_CLASS); + if (PTAConfig.v().getOutConfig().dumpJimple) { + String jimplePath = PTAConfig.v().getAppConfig().APP_PATH.replace(".jar", ""); + PTAUtils.dumpJimple(pta.getScene(), jimplePath); + System.out.println("Jimple files have been dumped to: " + jimplePath); + } + pta.run(); + return pta; + } + + public static void mainRun(String[] args) { + Stopwatch ptaTimer = Stopwatch.newAndStart("Main PTA (including pre-analysis)"); + String jvmName = ManagementFactory.getRuntimeMXBean().getName(); + String s = jvmName.split("@")[0]; + long pid = Long.parseLong(s); + MemoryWatcher memoryWatcher = new MemoryWatcher(pid, "Main PTA"); + memoryWatcher.start(); + run(args); + ptaTimer.stop(); + System.out.println(ptaTimer); + memoryWatcher.stop(); + System.out.println(memoryWatcher); + } + + public static void main(String[] args) { + mainRun(args); + } +} diff --git a/sootup.qilin/src/main/java/qilin/driver/PTAFactory.java b/sootup.qilin/src/main/java/qilin/driver/PTAFactory.java new file mode 100644 index 00000000000..89c5d9c34e1 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/driver/PTAFactory.java @@ -0,0 +1,199 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.driver; + +import qilin.core.PTA; +import qilin.core.PTAScene; +import qilin.parm.ctxcons.*; +import qilin.pta.PTAConfig; +import qilin.pta.tools.*; +import sootup.core.views.View; + +public class PTAFactory { + public static PTA createPTA(PTAPattern ptaPattern, View view, String mainClassSig) { + PTAScene scene = new PTAScene(view, mainClassSig); + switch (ptaPattern.getContextKind()) { + case HYBOBJ: + { + switch (ptaPattern.getApproach()) { + case DATADRIVEN: + { + // data-driven hybrid-2obj, Sehun Jeong oopsla'17 + CtxConstructor ctxCons = new HybObjCtxConstructor(); + return new DataDrivenPTA(scene, ctxCons); + } + case TUNNELING: + { + CtxConstructor ctxCons = new HybObjCtxConstructor(); + return new TunnelingPTA( + scene, ctxCons, ptaPattern.getContextDepth(), ptaPattern.getHeapContextDepth()); + } + default: + { + // static method using callsite as context, Yannis pldi'13 + return new HybridObjectSensPTA( + scene, ptaPattern.getContextDepth(), ptaPattern.getHeapContextDepth()); + } + } + } + case OBJECT: + { + switch (ptaPattern.getApproach()) { + case EAGLE: + { + // k-obj pointer analysis with Eagle pre-analysis, Jingbo OOPSLA'19 + assert ptaPattern.getContextDepth() == ptaPattern.getHeapContextDepth() + 1; + BasePTA eagle = new EaglePTA(scene, ptaPattern.getContextDepth()); + if (PTAConfig.v().getPtaConfig().ctxDebloating) { + return new DebloatedPTA(eagle, PTAConfig.v().getPtaConfig().debloatApproach); + } else { + return eagle; + } + } + case BEAN: + { + CtxConstructor ctxCons = new ObjCtxConstructor(); + return new BeanPTA(scene, ctxCons); + } + case TURNER: + { + return new TurnerPTA(scene, ptaPattern.getContextDepth()); + } + case ZIPPER: + { + CtxConstructor ctxCons = new ObjCtxConstructor(); + BasePTA zipperPTA = + new ZipperPTA( + scene, + ptaPattern.getContextDepth(), + ptaPattern.getHeapContextDepth(), + ctxCons); + if (PTAConfig.v().getPtaConfig().ctxDebloating) { + return new DebloatedPTA(zipperPTA, PTAConfig.v().getPtaConfig().debloatApproach); + } else { + return zipperPTA; + } + } + case MAHJONG: + { + CtxConstructor ctxCons = new ObjCtxConstructor(); + BasePTA mahjongPTA = + new MahjongPTA( + scene, + ptaPattern.getContextDepth(), + ptaPattern.getHeapContextDepth(), + ctxCons); + if (PTAConfig.v().getPtaConfig().ctxDebloating) { + return new DebloatedPTA(mahjongPTA, PTAConfig.v().getPtaConfig().debloatApproach); + } else { + return mahjongPTA; + } + } + case DATADRIVEN: + { + CtxConstructor ctxCons = new ObjCtxConstructor(); + return new DataDrivenPTA(scene, ctxCons); + } + case TUNNELING: + { + CtxConstructor ctxCons = new ObjCtxConstructor(); + return new TunnelingPTA( + scene, ctxCons, ptaPattern.getContextDepth(), ptaPattern.getHeapContextDepth()); + } + default: + { + BasePTA kobj = + new ObjectSensPTA( + scene, ptaPattern.getContextDepth(), ptaPattern.getHeapContextDepth()); + + if (PTAConfig.v().getPtaConfig().ctxDebloating) { + return new DebloatedPTA(kobj, PTAConfig.v().getPtaConfig().debloatApproach); + } else { + // normal object-sensitive pointer analysis, Milanova TOSEM'05 + return kobj; + } + } + } + } + case TYPE: + { + switch (ptaPattern.getApproach()) { + case DATADRIVEN: + { + CtxConstructor ctxCons = new TypeCtxConstructor(); + return new DataDrivenPTA(scene, ctxCons); + } + case TUNNELING: + { + CtxConstructor ctxCons = new TypeCtxConstructor(); + return new TunnelingPTA( + scene, ctxCons, ptaPattern.getContextDepth(), ptaPattern.getHeapContextDepth()); + } + default: + { + // normal type-sensitive pointer analysis, Yannis popl'11 + return new TypeSensPTA( + scene, ptaPattern.getContextDepth(), ptaPattern.getHeapContextDepth()); + } + } + } + case CALLSITE: + { + switch (ptaPattern.getApproach()) { + case ZIPPER: + { + CtxConstructor ctxCons = new CallsiteCtxConstructor(); + return new ZipperPTA( + scene, ptaPattern.getContextDepth(), ptaPattern.getHeapContextDepth(), ctxCons); + } + case MAHJONG: + { + CtxConstructor ctxCons = new CallsiteCtxConstructor(); + return new MahjongPTA( + scene, ptaPattern.getContextDepth(), ptaPattern.getHeapContextDepth(), ctxCons); + } + case DATADRIVEN: + { + CtxConstructor ctxCons = new CallsiteCtxConstructor(); + return new DataDrivenPTA(scene, ctxCons); + } + case TUNNELING: + { + CtxConstructor ctxCons = new CallsiteCtxConstructor(); + return new TunnelingPTA( + scene, ctxCons, ptaPattern.getContextDepth(), ptaPattern.getHeapContextDepth()); + } + case SELECTX: + { + return new SelectxPTA(scene, ptaPattern.getContextDepth()); + } + default: + { + // CallSite Sensitive + return new CallSiteSensPTA( + scene, ptaPattern.getContextDepth(), ptaPattern.getHeapContextDepth()); + } + } + } + case INSENS: + default: + return new Spark(scene); + } + } +} diff --git a/sootup.qilin/src/main/java/qilin/driver/PTAOption.java b/sootup.qilin/src/main/java/qilin/driver/PTAOption.java new file mode 100644 index 00000000000..e8e77952e90 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/driver/PTAOption.java @@ -0,0 +1,251 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.driver; + +import java.util.*; +import org.apache.commons.cli.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import qilin.pta.PTAConfig; +import qilin.pta.toolkits.turner.Turner; +import qilin.pta.tools.DebloatedPTA; + +public class PTAOption extends Options { + private static final Logger logger = LoggerFactory.getLogger(PTAOption.class); + + /** add option "-brief -option" with description */ + protected void addOption(String brief, String option, String description) { + addOption(new Option(brief, option, false, description)); + } + + /** add option "-brief -option <arg>" with description */ + protected void addOption(String brief, String option, String arg, String description) { + addOption( + OptionBuilder.withLongOpt(option) + .withArgName(arg) + .hasArg() + .withDescription(description) + .create(brief)); + } + + public PTAOption() { + // input configurations + addOption( + "app", + "apppath", + "dir or jar", + "The directory containing the classes for the application or the application jar file (default: .)"); + addOption( + "lib", + "libpath", + "dir or jar", + "The directory containing the library jar files for the application or the library jar file"); + addOption( + null, + "includeall", + "Include packages which are not analyzed by default. (default value: false)"); + addOption( + null, + "exclude", + "packages", + "Exclude selected packages. (delimiting symbol: semicolon ';')"); + addOption( + null, + "jre", + "dir", + "The directory containing the version of JRE to be used for whole program analysis"); + addOption( + "reflog", + "reflectionlog", + "file", + "The reflection log file for the application for resolving reflective call sites"); + addOption( + "main", + "mainclass", + "class name", + "Name of the main class for the application (must be specified when appmode)"); + + // output configurations. + addOption("jimple", "dumpjimple", "Dump appclasses to jimple. (default value: false)"); + addOption("stats", "dumpstats", "Dump statistics into files. (default value: false)"); + addOption( + "ptsall", + "dumpallpts", + "Dump points-to of lib vars results to output/pts.txt (default value: false)"); + addOption("pts", "dumppts", "Dump points-to results to output/pts.txt (default value: false)"); + + // general PTA configurations + addOption( + "clinit", "clinitmode", "APP|FULL|ONFLY", "clinit methods loading mode, default: ONFLY"); + addOption( + "mh", + "mergeheap", + "merge heaps of StringBuilder/StringBuffer/Throwable (default value: false)"); + addOption( + "lcs", + "emptycontextforignoretypes", + "Limit heap context to 0 for Strings/Exceptions in PTA (default value: false)"); + addOption( + "pta", + "pointstoanalysis", + "(c|o)+?(h)?|insens", + "Specify Pointer Analysis e.g. 2o1h or 2o -> 2obj+1heap (default value: insens; default h: k-1.)"); + addOption( + "se", + "singleentry", + "A lightweight mode with only one main method entry. (default value: false)"); + addOption("sc", "stringconstants", "Propagate all string constants (default value: false)"); + addOption("pae", "precisearray", "Enable precise Array Element type (default value: false)"); + addOption( + "pe", "preciseexceptions", "Enable precisely handling exceptions (default value: false)"); + + // a specific PTA's configuration + addOption( + "tc", + "turnerconfig", + "[DEFAULT, PHASE_ONE, PHASE_TWO]", + "Run Turner in the given setting (default value: DEFAULT)"); + addOption("cd", "ctxdebloat", "Enable context debloating optimization (default value: false)"); + addOption( + "cda", + "debloatapproach", + "[CONCH, DEBLOATERX]", + "Specify debloating approach (default value: CONCH)"); + addOption("tmd", "modular", "Enable Turner to run modularly (default value: false)"); + + // callgraph algorithm configurations + addOption( + "cga", + "callgraphalg", + "[CHA, VTA, RTA, SPARK, GEOM, QILIN]", + "Specify callgraph construction algorithm (default value: QILIN)"); + + // others + addOption("h", "help", "print this message"); + addOption("pre", "preonly", "Run only pre-analysis (default value: false)"); + } + + public void parseCommandLine(String[] args) { + try { + CommandLine cmd = new GnuParser().parse(this, args); + if (cmd.hasOption("help")) { + new HelpFormatter().printHelp("qilin", this); + System.exit(0); + } + parseCommandLineOptions(cmd); + } catch (Exception e) { + logger.error("Error parsing command line options", e); + System.exit(1); + } + } + + /** + * Set all variables from the command line arguments. + * + * @param cmd + */ + protected void parseCommandLineOptions(CommandLine cmd) { + // pointer analysis configuration + if (cmd.hasOption("apppath")) { + PTAConfig.v().getAppConfig().APP_PATH = cmd.getOptionValue("apppath"); + } + String ptacmd = cmd.hasOption("pta") ? cmd.getOptionValue("pta") : "insens"; + PTAConfig.v().getPtaConfig().ptaPattern = new PTAPattern(ptacmd); + PTAConfig.v().getPtaConfig().ptaName = PTAConfig.v().getPtaConfig().ptaPattern.toString(); + if (cmd.hasOption("singleentry")) { + PTAConfig.v().getPtaConfig().singleentry = true; + } + if (cmd.hasOption("mergeheap")) { + PTAConfig.v().getPtaConfig().mergeHeap = true; + } + if (cmd.hasOption("stringconstants")) { + PTAConfig.v().getPtaConfig().stringConstants = true; + } + if (cmd.hasOption("emptycontextforignoretypes")) { + PTAConfig.v().getPtaConfig().enforceEmptyCtxForIgnoreTypes = true; + } + if (cmd.hasOption("clinitmode")) { + PTAConfig.v().getPtaConfig().clinitMode = + PTAConfig.ClinitMode.valueOf(cmd.getOptionValue("clinitmode")); + } + if (cmd.hasOption("preonly")) { + PTAConfig.v().getPtaConfig().preAnalysisOnly = true; + } + if (cmd.hasOption("ctxdebloat")) { + PTAConfig.v().getPtaConfig().ctxDebloating = true; + if (cmd.hasOption("debloatapproach")) { + PTAConfig.v().getPtaConfig().debloatApproach = + DebloatedPTA.DebloatApproach.valueOf(cmd.getOptionValue("debloatapproach")); + } + } + if (cmd.hasOption("preciseexceptions")) { + PTAConfig.v().getPtaConfig().preciseExceptions = true; + } + if (cmd.hasOption("modular")) { + Turner.isModular = true; + } + if (cmd.hasOption("precisearray")) { + PTAConfig.v().getPtaConfig().preciseArrayElement = true; + } + // application configuration + if (cmd.hasOption("mainclass")) { + PTAConfig.v().getAppConfig().MAIN_CLASS = cmd.getOptionValue("mainclass"); + } + if (cmd.hasOption("jre")) { + PTAConfig.v().getAppConfig().JRE = cmd.getOptionValue("jre"); + } + if (cmd.hasOption("libpath")) { + PTAConfig.v().getAppConfig().LIB_PATH = cmd.getOptionValue("libpath"); + } + if (cmd.hasOption("exclude")) { + PTAConfig.v().getAppConfig().EXCLUDE = parsePackages(cmd.getOptionValue("exclude")); + } + if (cmd.hasOption("reflectionlog")) { + PTAConfig.v().getAppConfig().REFLECTION_LOG = cmd.getOptionValue("reflectionlog"); + } + if (cmd.hasOption("inlcudeall")) { + PTAConfig.v().getAppConfig().INCLUDE_ALL = true; + } + if (cmd.hasOption("turnerconfig")) { + PTAConfig.v().turnerConfig = + PTAConfig.TurnerConfig.valueOf(cmd.getOptionValue("turnerconfig")); + } + // output + if (cmd.hasOption("dumpjimple")) { + PTAConfig.v().getOutConfig().dumpJimple = true; + } + if (cmd.hasOption("dumppts")) { + PTAConfig.v().getOutConfig().dumppts = true; + } + if (cmd.hasOption("dumpallpts")) { + PTAConfig.v().getOutConfig().dumppts = true; + PTAConfig.v().getOutConfig().dumplibpts = true; + } + if (cmd.hasOption("dumpstats")) { + PTAConfig.v().getOutConfig().dumpStats = true; + } + } + + static List parsePackages(String packagesString) { + ArrayList pkgList = new ArrayList<>(); + String[] pkgs = packagesString.split(";"); + Collections.addAll(pkgList, pkgs); + return pkgList; + } +} diff --git a/sootup.qilin/src/main/java/qilin/driver/PTAPattern.java b/sootup.qilin/src/main/java/qilin/driver/PTAPattern.java new file mode 100644 index 00000000000..4a621e73e8c --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/driver/PTAPattern.java @@ -0,0 +1,263 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.driver; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import qilin.util.Util; + +public class PTAPattern { + private ContextKind ctxKind; + private Approach approach; + private int k, hk; + + public PTAPattern(String ptacmd) { + parsePTACommand(ptacmd); + } + + private Pattern getSupportPTAPattern() { + String approachPtn = "(" + String.join("|", Approach.approachAliases()) + ")"; + String ctxLenPtn = "(\\d*)"; + String ctxKindPtn = "(" + String.join("|", ContextKind.contextAliases()) + ")"; + String heapLenPtn = "(\\+?(\\d*)h(eap)?)?"; + String regexPattern = "^(" + approachPtn + "-)?" + ctxLenPtn + ctxKindPtn + heapLenPtn + "$"; + return Pattern.compile(regexPattern); + } + + private void parsePTACommand(String ptacmd) { + Pattern pattern = getSupportPTAPattern(); + Matcher matcher = pattern.matcher(ptacmd); + if (!matcher.find()) { + throw new RuntimeException("Unsupported PTA: " + ptacmd + " !"); + } + + String approachString = matcher.group(2); + String kString = matcher.group(3); + String typeString = matcher.group(4); + String hkString = matcher.group(6); + + approach = Approach.toApproach(approachString); + k = kString.equals("") ? 1 : Integer.parseInt(kString); + ctxKind = ContextKind.toCtxKind(typeString); + hk = hkString == null ? -1 : hkString.equals("") ? 1 : Integer.parseInt(hkString); + + if (k == 0) { + ctxKind = ContextKind.INSENS; + } + if (hk == -1) { + hk = k - 1; + } + validateApproachCompatibility(); + validateContextLength(); + } + + private void validateApproachCompatibility() { + switch (ctxKind) { + case HYBOBJ: + if (approach != Approach.DATADRIVEN + && approach != Approach.NONE + && approach != Approach.TUNNELING) { + throw new RuntimeException( + "Approach <" + + approach.toString() + + " > is currently not designed for hybrid sensitivity"); + } + break; + case HYBTYPE: + if (approach != Approach.NONE) { + throw new RuntimeException( + "Approach <" + + approach.toString() + + " > is currently not designed for hybrid sensitivity"); + } + break; + case TYPE: + { + if (approach != Approach.NONE && approach != Approach.TUNNELING) { + throw new RuntimeException( + "Approach <" + + approach.toString() + + "> is currently not designed for type sensitivity."); + } + } + break; + case CALLSITE: + { + if (approach != Approach.NONE + && approach != Approach.ZIPPER + && approach != Approach.MAHJONG + && approach != Approach.DATADRIVEN + && approach != Approach.TUNNELING + && approach != Approach.SELECTX) { + throw new RuntimeException( + "Approach <" + + approach.toString() + + "> is currently not designed for call-site sensitivity."); + } + } + break; + case OBJECT: + { + if (approach != Approach.EAGLE + && approach != Approach.ZIPPER + && approach != Approach.TURNER + && approach != Approach.DATADRIVEN + && approach != Approach.TUNNELING + && approach != Approach.MERCURIAL + && approach != Approach.MAHJONG + && approach != Approach.BEAN + && approach != Approach.NONE) { + throw new RuntimeException( + "Approach <" + + approach.toString() + + "> is currently not designed for object sensitivity."); + } + } + break; + case INSENS: + { + if (approach != Approach.SPARK && approach != Approach.NONE) { + throw new RuntimeException( + "Approach <" + + approach.toString() + + "> is currently not designed for context insensitive pointer analysis."); + } + } + default: + break; + } + } + + private void validateContextLength() { + if (ctxKind == ContextKind.INSENS) { + return; + } + if (hk > k) { + throw new RuntimeException("Heap context depth cannot exceed method context depth!"); + } + if (hk < k - 1 && (ctxKind == ContextKind.OBJECT || ctxKind == ContextKind.TYPE)) { + throw new RuntimeException( + "Heap context depth can only be k or k-1 for object/type-sensitive analysis!"); + } + } + + public ContextKind getContextKind() { + return ctxKind; + } + + public Approach getApproach() { + return approach; + } + + public int getContextDepth() { + return k; + } + + public int getHeapContextDepth() { + return hk; + } + + @Override + public String toString() { + if (ctxKind == ContextKind.INSENS) { + return "insensitive"; + } + StringBuilder builder = new StringBuilder(); + if (!approach.toString().isEmpty()) { + builder.append(approach); + builder.append('-'); + } + builder.append(k); + builder.append(ctxKind.toString()); + builder.append('+'); + builder.append(hk); + builder.append("heap"); + return builder.toString(); + } + + public enum Approach { + // for insens + NONE, + SPARK, + // + BEAN, + ZIPPER, + EAGLE, + TURNER, + DATADRIVEN, + TUNNELING, + MERCURIAL, + MAHJONG, + SELECTX; + + static final Map approaches = new HashMap<>(); + + static { + Util.add(approaches, BEAN, "bean", "B"); + Util.add(approaches, ZIPPER, "zipper", "Z"); + Util.add(approaches, EAGLE, "eagle", "E"); + Util.add(approaches, TURNER, "turner", "T"); + Util.add(approaches, MERCURIAL, "mercurial", "hg"); + Util.add(approaches, MAHJONG, "mahjong", "M"); + Util.add(approaches, DATADRIVEN, "datadriven", "D"); + Util.add(approaches, TUNNELING, "tunneling", "t"); + Util.add(approaches, SELECTX, "selectx", "s"); + Util.add(approaches, SPARK, "insens", "ci"); + } + + public static Collection approachAliases() { + return approaches.keySet(); + } + + public static Approach toApproach(String name) { + return approaches.getOrDefault(name, NONE); + } + + @Override + public String toString() { + switch (this) { + case DATADRIVEN: + return "data-driven"; + case TUNNELING: + return "tunneling"; + case BEAN: + return "bean"; + case ZIPPER: + return "zipper"; + case EAGLE: + return "eagle"; + case TURNER: + return "turner"; + case MERCURIAL: + return "mercurial"; + case MAHJONG: + return "mahjong"; + case SELECTX: + return "selectx"; + case SPARK: + return "spark"; + default: + return ""; + } + } + } +} diff --git a/sootup.qilin/src/main/java/qilin/parm/ctxcons/CallsiteCtxConstructor.java b/sootup.qilin/src/main/java/qilin/parm/ctxcons/CallsiteCtxConstructor.java new file mode 100644 index 00000000000..1bf7fe627ba --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/parm/ctxcons/CallsiteCtxConstructor.java @@ -0,0 +1,44 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.parm.ctxcons; + +import qilin.core.context.Context; +import qilin.core.context.ContextElement; +import qilin.core.context.ContextElements; +import qilin.core.pag.CallSite; +import qilin.core.pag.ContextAllocNode; +import qilin.core.pag.ContextMethod; +import sootup.core.model.SootMethod; + +public class CallsiteCtxConstructor implements CtxConstructor { + + @Override + public Context constructCtx( + ContextMethod caller, ContextAllocNode receiverNode, CallSite callSite, SootMethod target) { + Context callerContext = caller.context(); + assert callerContext instanceof ContextElements; + ContextElements ctxElems = (ContextElements) callerContext; + int s = ctxElems.size(); + ContextElement[] cxt = ctxElems.getElements(); + ContextElement[] array = new ContextElement[s + 1]; + array[0] = callSite; + System.arraycopy(cxt, 0, array, 1, s); + return new ContextElements(array, s + 1); + } +} diff --git a/sootup.qilin/src/main/java/qilin/parm/ctxcons/CtxConstructor.java b/sootup.qilin/src/main/java/qilin/parm/ctxcons/CtxConstructor.java new file mode 100644 index 00000000000..e6393add92d --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/parm/ctxcons/CtxConstructor.java @@ -0,0 +1,34 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.parm.ctxcons; + +import qilin.core.context.Context; +import qilin.core.context.ContextElement; +import qilin.core.context.ContextElements; +import qilin.core.pag.CallSite; +import qilin.core.pag.ContextAllocNode; +import qilin.core.pag.ContextMethod; +import sootup.core.model.SootMethod; + +public interface CtxConstructor { + Context constructCtx( + ContextMethod caller, ContextAllocNode receiverNode, CallSite callSite, SootMethod target); + + Context emptyContext = new ContextElements(new ContextElement[0], 0); +} diff --git a/sootup.qilin/src/main/java/qilin/parm/ctxcons/HybObjCtxConstructor.java b/sootup.qilin/src/main/java/qilin/parm/ctxcons/HybObjCtxConstructor.java new file mode 100644 index 00000000000..caf05bc75da --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/parm/ctxcons/HybObjCtxConstructor.java @@ -0,0 +1,70 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.parm.ctxcons; + +import qilin.core.context.Context; +import qilin.core.context.ContextElement; +import qilin.core.context.ContextElements; +import qilin.core.pag.CallSite; +import qilin.core.pag.ContextAllocNode; +import qilin.core.pag.ContextMethod; +import sootup.core.model.SootMethod; + +// implementation of selective hybrid context...(Yannis pldi'13) +public class HybObjCtxConstructor implements CtxConstructor { + /* + * Support Sb-1obj, S-2obj+H, ... + * */ + @Override + public Context constructCtx( + ContextMethod caller, ContextAllocNode receiverNode, CallSite callSite, SootMethod target) { + Context callerContext = caller.context(); + if (receiverNode == null) { // static invoke + assert callerContext instanceof ContextElements; + ContextElements callerCtx = (ContextElements) callerContext; + ContextElement[] cxtAllocs = callerCtx.getElements(); + int s = callerCtx.size(); + ContextElement[] array; + if (s >= 1) { + array = new ContextElement[s + 1]; + array[1] = callSite; + array[0] = cxtAllocs[0]; + if (s > 1) { + System.arraycopy(cxtAllocs, 1, array, 2, s - 1); + } + return new ContextElements(array, s + 1); + } else { + array = new ContextElement[2]; + array[1] = callSite; + array[0] = null; + return new ContextElements(array, 2); + } + } else { + Context context = receiverNode.context(); + assert context instanceof ContextElements; + ContextElements ctx = (ContextElements) context; + int s = ctx.size(); + ContextElement[] cxtAllocs = ctx.getElements(); + ContextElement[] array = new ContextElement[s + 1]; + array[0] = receiverNode.base(); + System.arraycopy(cxtAllocs, 0, array, 1, s); + return new ContextElements(array, s + 1); + } + } +} diff --git a/sootup.qilin/src/main/java/qilin/parm/ctxcons/HybTypeCtxConstructor.java b/sootup.qilin/src/main/java/qilin/parm/ctxcons/HybTypeCtxConstructor.java new file mode 100644 index 00000000000..669d7ff9e8e --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/parm/ctxcons/HybTypeCtxConstructor.java @@ -0,0 +1,61 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.parm.ctxcons; + +import qilin.core.context.Context; +import qilin.core.context.ContextElement; +import qilin.core.context.ContextElements; +import qilin.core.context.TypeContextElement; +import qilin.core.pag.CallSite; +import qilin.core.pag.ContextAllocNode; +import qilin.core.pag.ContextMethod; +import sootup.core.model.SootMethod; + +// implementation of selective hybrid context...(Yannis pldi'13) +public class HybTypeCtxConstructor implements CtxConstructor { + + @Override + public Context constructCtx( + ContextMethod caller, ContextAllocNode receiverNode, CallSite callSite, SootMethod target) { + Context callerContext = caller.context(); + if (receiverNode == null) { // static invoke + assert callerContext instanceof ContextElements; + ContextElements callerCtxs = (ContextElements) callerContext; + ContextElement[] cxtAllocs = callerCtxs.getElements(); + int s = callerCtxs.size(); + ContextElement[] array = new ContextElement[s + 1]; + array[1] = callSite; + array[0] = cxtAllocs[0]; + if (s > 1) { + System.arraycopy(cxtAllocs, 1, array, 2, s - 1); + } + return new ContextElements(array, s + 1); + } else { + Context context = receiverNode.context(); + assert context instanceof ContextElements; + ContextElements ctx = (ContextElements) context; + int s = ctx.size(); + ContextElement[] cxtAllocs = ctx.getElements(); + ContextElement[] array = new ContextElement[s + 1]; + array[0] = TypeContextElement.getTypeContextElement(receiverNode.base()); + System.arraycopy(cxtAllocs, 0, array, 1, s); + return new ContextElements(array, s + 1); + } + } +} diff --git a/sootup.qilin/src/main/java/qilin/parm/ctxcons/InsensCtxConstructor.java b/sootup.qilin/src/main/java/qilin/parm/ctxcons/InsensCtxConstructor.java new file mode 100644 index 00000000000..27fd8e7715a --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/parm/ctxcons/InsensCtxConstructor.java @@ -0,0 +1,34 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.parm.ctxcons; + +import qilin.core.context.Context; +import qilin.core.pag.CallSite; +import qilin.core.pag.ContextAllocNode; +import qilin.core.pag.ContextMethod; +import sootup.core.model.SootMethod; + +public class InsensCtxConstructor implements CtxConstructor { + + @Override + public Context constructCtx( + ContextMethod caller, ContextAllocNode receiverNode, CallSite callSite, SootMethod target) { + return emptyContext; + } +} diff --git a/sootup.qilin/src/main/java/qilin/parm/ctxcons/ObjCtxConstructor.java b/sootup.qilin/src/main/java/qilin/parm/ctxcons/ObjCtxConstructor.java new file mode 100644 index 00000000000..fdd1a45c9fb --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/parm/ctxcons/ObjCtxConstructor.java @@ -0,0 +1,49 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.parm.ctxcons; + +import qilin.core.context.Context; +import qilin.core.context.ContextElement; +import qilin.core.context.ContextElements; +import qilin.core.pag.CallSite; +import qilin.core.pag.ContextAllocNode; +import qilin.core.pag.ContextMethod; +import sootup.core.model.SootMethod; + +// implementation of obj context...(Ana Tosem'05) +public class ObjCtxConstructor implements CtxConstructor { + + @Override + public Context constructCtx( + ContextMethod caller, ContextAllocNode receiverNode, CallSite callSite, SootMethod target) { + Context callerContext = caller.context(); + if (receiverNode == null) { // static invoke + return callerContext; + } + Context context = receiverNode.context(); + assert context instanceof ContextElements; + ContextElements ctxElems = (ContextElements) context; + int s = ctxElems.size(); + ContextElement[] cxtAllocs = ctxElems.getElements(); + ContextElement[] array = new ContextElement[s + 1]; + array[0] = receiverNode.base(); + System.arraycopy(cxtAllocs, 0, array, 1, s); + return new ContextElements(array, s + 1); + } +} diff --git a/sootup.qilin/src/main/java/qilin/parm/ctxcons/TypeCtxConstructor.java b/sootup.qilin/src/main/java/qilin/parm/ctxcons/TypeCtxConstructor.java new file mode 100644 index 00000000000..45c9bcd315a --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/parm/ctxcons/TypeCtxConstructor.java @@ -0,0 +1,52 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.parm.ctxcons; + +import qilin.core.context.Context; +import qilin.core.context.ContextElement; +import qilin.core.context.ContextElements; +import qilin.core.context.TypeContextElement; +import qilin.core.pag.AllocNode; +import qilin.core.pag.CallSite; +import qilin.core.pag.ContextAllocNode; +import qilin.core.pag.ContextMethod; +import sootup.core.model.SootMethod; + +// implementation of type context...(Yannis popl'11) +public class TypeCtxConstructor implements CtxConstructor { + + @Override + public Context constructCtx( + ContextMethod caller, ContextAllocNode receiverNode, CallSite callSite, SootMethod target) { + Context callerContext = caller.context(); + if (receiverNode == null) { // static invoke + return callerContext; + } + Context context = receiverNode.context(); + assert context instanceof ContextElements; + ContextElements ctxElems = (ContextElements) context; + int s = ctxElems.size(); + ContextElement[] cxtAllocs = ctxElems.getElements(); + ContextElement[] array = new ContextElement[s + 1]; + AllocNode base = receiverNode.base(); + array[0] = TypeContextElement.getTypeContextElement(base); + System.arraycopy(cxtAllocs, 0, array, 1, s); + return new ContextElements(array, s + 1); + } +} diff --git a/sootup.qilin/src/main/java/qilin/parm/heapabst/AllocSiteAbstractor.java b/sootup.qilin/src/main/java/qilin/parm/heapabst/AllocSiteAbstractor.java new file mode 100644 index 00000000000..df01abf3f36 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/parm/heapabst/AllocSiteAbstractor.java @@ -0,0 +1,28 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.parm.heapabst; + +import qilin.core.pag.AllocNode; + +public class AllocSiteAbstractor implements HeapAbstractor { + @Override + public AllocNode abstractHeap(AllocNode heap) { + return heap; + } +} diff --git a/sootup.qilin/src/main/java/qilin/parm/heapabst/HeapAbstractor.java b/sootup.qilin/src/main/java/qilin/parm/heapabst/HeapAbstractor.java new file mode 100644 index 00000000000..70cb0eec8a4 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/parm/heapabst/HeapAbstractor.java @@ -0,0 +1,25 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.parm.heapabst; + +import qilin.core.pag.AllocNode; + +public interface HeapAbstractor { + AllocNode abstractHeap(AllocNode heap); +} diff --git a/sootup.qilin/src/main/java/qilin/parm/heapabst/HeuristicAbstractor.java b/sootup.qilin/src/main/java/qilin/parm/heapabst/HeuristicAbstractor.java new file mode 100644 index 00000000000..87645200d9b --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/parm/heapabst/HeuristicAbstractor.java @@ -0,0 +1,52 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.parm.heapabst; + +import java.util.Set; +import qilin.core.pag.AllocNode; +import qilin.core.pag.MergedNewExpr; +import qilin.core.pag.PAG; +import qilin.util.DataFactory; +import qilin.util.PTAUtils; +import sootup.core.types.ReferenceType; +import sootup.core.types.Type; +import sootup.core.views.View; + +public class HeuristicAbstractor implements HeapAbstractor { + private final PAG pag; + private final View view; + private final Set mergedTypes = DataFactory.createSet(); + + public HeuristicAbstractor(PAG pag) { + this.pag = pag; + this.view = pag.getPta().getView(); + mergedTypes.add(PTAUtils.getClassType("java.lang.StringBuffer")); + mergedTypes.add(PTAUtils.getClassType("java.lang.StringBuilder")); + } + + @Override + public AllocNode abstractHeap(AllocNode heap) { + Type type = heap.getType(); + if (mergedTypes.contains(type) || (PTAUtils.isThrowable(view, type) && mergedTypes.add(type))) { + return pag.makeAllocNode(MergedNewExpr.v((ReferenceType) type), type, null); + } else { + return heap; + } + } +} diff --git a/sootup.qilin/src/main/java/qilin/parm/heapabst/MahjongAbstractor.java b/sootup.qilin/src/main/java/qilin/parm/heapabst/MahjongAbstractor.java new file mode 100644 index 00000000000..295eea1766a --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/parm/heapabst/MahjongAbstractor.java @@ -0,0 +1,59 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.parm.heapabst; + +import java.util.Map; +import java.util.Set; +import qilin.core.pag.AllocNode; +import qilin.core.pag.PAG; +import qilin.util.PTAUtils; +import sootup.core.model.SootMethod; +import sootup.core.types.Type; +import sootup.core.views.View; + +public class MahjongAbstractor implements HeapAbstractor { + private final Set mergedHeap; + private final Map heapModelMap; + private final PAG pag; + private final View view; + + public MahjongAbstractor(PAG pag, Set mergedHeap, Map heapModelMap) { + this.pag = pag; + this.view = pag.getPta().getView(); + this.mergedHeap = mergedHeap; + this.heapModelMap = heapModelMap; + } + + @Override + public AllocNode abstractHeap(AllocNode heap) { + Object newExpr = heap.getNewExpr(); + Type type = heap.getType(); + SootMethod m = heap.getMethod(); + Object mergedIr = this.heapModelMap.get(newExpr); + if (this.mergedHeap.contains(mergedIr)) { + return pag.makeAllocNode(mergedIr, type, null); + } else { + if (PTAUtils.isThrowable(view, type)) { + // Mahjong still needs heuristics to handle throwable types. + return pag.makeAllocNode("Merged " + type, type, null); + } + return pag.makeAllocNode(newExpr, type, m); + } + } +} diff --git a/sootup.qilin/src/main/java/qilin/parm/select/BeanSelector.java b/sootup.qilin/src/main/java/qilin/parm/select/BeanSelector.java new file mode 100644 index 00000000000..fdcef6ca8f6 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/parm/select/BeanSelector.java @@ -0,0 +1,85 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.parm.select; + +import java.util.Map; +import qilin.core.context.Context; +import qilin.core.context.ContextElement; +import qilin.core.context.ContextElements; +import qilin.core.pag.AllocNode; +import qilin.core.pag.FieldValNode; +import qilin.core.pag.LocalVarNode; +import qilin.core.pag.PAG; +import sootup.core.model.SootMethod; + +public class BeanSelector extends CtxSelector { + private final PAG pag; + private final Map>> beanNexCtxMap; + // currently, we only support k = 2 and hk = 1; + // we will generalize Bean in future. + private final int k = 2; + private final int hk = 1; + + public BeanSelector(PAG pag, Map>> beanNexCtxMap) { + this.pag = pag; + this.beanNexCtxMap = beanNexCtxMap; + } + + @Override + public Context select(SootMethod m, Context context) { + return contextTailor(context, k); + } + + @Override + public Context select(LocalVarNode lvn, Context context) { + return contextTailor(context, k); + } + + @Override + public Context select(FieldValNode fvn, Context context) { + return contextTailor(context, k); + } + + @Override + public Context select(AllocNode heap, Context context) { + assert context instanceof ContextElements; + ContextElements ctxElems = (ContextElements) context; + int s = ctxElems.size(); + if (s > 1) { + ContextElement[] cxtAllocs = ctxElems.getElements(); + AllocNode allocator = (AllocNode) cxtAllocs[0]; + if (beanNexCtxMap.containsKey(heap.getNewExpr())) { + Map> mMap1 = beanNexCtxMap.get(heap.getNewExpr()); + if (mMap1.containsKey(allocator.getNewExpr())) { + Map mMap2 = mMap1.get(allocator.getNewExpr()); + AllocNode allocAllocNode = (AllocNode) cxtAllocs[1]; + if (allocAllocNode != null && mMap2.containsKey(allocAllocNode.getNewExpr())) { + Object newCtxNode = mMap2.get(allocAllocNode.getNewExpr()); + AllocNode newCtxAllocNode = pag.getAllocNode(newCtxNode); + ContextElement[] array = new ContextElement[s]; + System.arraycopy(cxtAllocs, 0, array, 0, s); + array[0] = newCtxAllocNode; + context = new ContextElements(array, s); + } + } + } + } + return contextTailor(context, hk); + } +} diff --git a/sootup.qilin/src/main/java/qilin/parm/select/CtxSelector.java b/sootup.qilin/src/main/java/qilin/parm/select/CtxSelector.java new file mode 100644 index 00000000000..af7421fac48 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/parm/select/CtxSelector.java @@ -0,0 +1,52 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.parm.select; + +import qilin.core.context.Context; +import qilin.core.context.ContextElement; +import qilin.core.context.ContextElements; +import qilin.core.pag.AllocNode; +import qilin.core.pag.FieldValNode; +import qilin.core.pag.LocalVarNode; +import qilin.parm.ctxcons.CtxConstructor; +import sootup.core.model.SootMethod; + +public abstract class CtxSelector { + public abstract Context select(SootMethod m, Context context); + + public abstract Context select(LocalVarNode lvn, Context context); + + public abstract Context select(FieldValNode fvn, Context context); + + public abstract Context select(AllocNode heap, Context context); + + protected Context contextTailor(Context context, int length) { + if (length == 0) { + return CtxConstructor.emptyContext; + } + ContextElements ctx = (ContextElements) context; + ContextElement[] fullContexts = ctx.getElements(); + if (length >= ctx.size()) { + return context; + } + ContextElement[] newContexts = new ContextElement[length]; + System.arraycopy(fullContexts, 0, newContexts, 0, length); + return new ContextElements(newContexts, length); + } +} diff --git a/sootup.qilin/src/main/java/qilin/parm/select/DebloatingSelector.java b/sootup.qilin/src/main/java/qilin/parm/select/DebloatingSelector.java new file mode 100644 index 00000000000..f4498459eef --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/parm/select/DebloatingSelector.java @@ -0,0 +1,61 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.parm.select; + +import java.util.Set; +import qilin.core.context.Context; +import qilin.core.pag.AllocNode; +import qilin.core.pag.FieldValNode; +import qilin.core.pag.LocalVarNode; +import qilin.parm.ctxcons.CtxConstructor; +import sootup.core.model.SootMethod; + +public class DebloatingSelector extends CtxSelector { + + protected final Set ctxDepHeaps; + + public DebloatingSelector(Set ctxDepHeaps) { + this.ctxDepHeaps = ctxDepHeaps; + } + + @Override + public Context select(SootMethod m, Context context) { + return context; + } + + @Override + public Context select(LocalVarNode lvn, Context context) { + return context; + } + + @Override + public Context select(FieldValNode fvn, Context context) { + return context; + } + + @Override + public Context select(AllocNode heap, Context context) { + Object ir = heap.getNewExpr(); + if (ctxDepHeaps.contains(ir)) { + return context; + } else { + return CtxConstructor.emptyContext; + } + } +} diff --git a/sootup.qilin/src/main/java/qilin/parm/select/FullMethodLvSelector.java b/sootup.qilin/src/main/java/qilin/parm/select/FullMethodLvSelector.java new file mode 100644 index 00000000000..50c6b4702ba --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/parm/select/FullMethodLvSelector.java @@ -0,0 +1,61 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.parm.select; + +import java.util.Map; +import qilin.core.context.Context; +import qilin.core.pag.AllocNode; +import qilin.core.pag.FieldValNode; +import qilin.core.pag.LocalVarNode; +import sootup.core.model.SootMethod; + +public class FullMethodLvSelector extends CtxSelector { + private final int k; + /* + * Methods and its corresponding context length obtained by Data-driven (OOPSLA 2017). + * */ + private final Map m2len; + + public FullMethodLvSelector(Map m2len, int k) { + this.m2len = m2len; + this.k = k; + } + + @Override + public Context select(SootMethod m, Context context) { + return contextTailor(context, m2len.getOrDefault(m, 0)); + } + + @Override + public Context select(LocalVarNode lvn, Context context) { + SootMethod sm = lvn.getMethod(); + return contextTailor(context, m2len.getOrDefault(sm, 0)); + } + + @Override + public Context select(FieldValNode fvn, Context context) { + return contextTailor(context, k); + } + + @Override + public Context select(AllocNode heap, Context context) { + SootMethod sm = heap.getMethod(); + return contextTailor(context, Math.max(0, m2len.getOrDefault(sm, 0) - 1)); + } +} diff --git a/sootup.qilin/src/main/java/qilin/parm/select/HeuristicSelector.java b/sootup.qilin/src/main/java/qilin/parm/select/HeuristicSelector.java new file mode 100644 index 00000000000..22382cb1c2d --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/parm/select/HeuristicSelector.java @@ -0,0 +1,60 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.parm.select; + +import qilin.core.context.Context; +import qilin.core.pag.AllocNode; +import qilin.core.pag.FieldValNode; +import qilin.core.pag.LocalVarNode; +import qilin.parm.ctxcons.CtxConstructor; +import qilin.util.PTAUtils; +import sootup.core.model.SootMethod; +import sootup.core.views.View; + +public class HeuristicSelector extends CtxSelector { + private final View view; + + public HeuristicSelector(View view) { + this.view = view; + } + + @Override + public Context select(SootMethod m, Context context) { + return context; + } + + @Override + public Context select(LocalVarNode lvn, Context context) { + return context; + } + + @Override + public Context select(FieldValNode fvn, Context context) { + return context; + } + + @Override + public Context select(AllocNode heap, Context context) { + if (PTAUtils.isThrowable(view, heap.getType()) + || PTAUtils.subtypeOfAbstractStringBuilder(heap.getType())) { + return CtxConstructor.emptyContext; + } + return context; + } +} diff --git a/sootup.qilin/src/main/java/qilin/parm/select/InsenSelector.java b/sootup.qilin/src/main/java/qilin/parm/select/InsenSelector.java new file mode 100644 index 00000000000..f1f9b495e41 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/parm/select/InsenSelector.java @@ -0,0 +1,48 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.parm.select; + +import qilin.core.context.Context; +import qilin.core.pag.AllocNode; +import qilin.core.pag.FieldValNode; +import qilin.core.pag.LocalVarNode; +import qilin.parm.ctxcons.CtxConstructor; +import sootup.core.model.SootMethod; + +public class InsenSelector extends CtxSelector { + @Override + public Context select(SootMethod m, Context context) { + return CtxConstructor.emptyContext; + } + + @Override + public Context select(LocalVarNode lvn, Context context) { + return CtxConstructor.emptyContext; + } + + @Override + public Context select(FieldValNode fvn, Context context) { + return contextTailor(context, 1); + } + + @Override + public Context select(AllocNode heap, Context context) { + return CtxConstructor.emptyContext; + } +} diff --git a/sootup.qilin/src/main/java/qilin/parm/select/PartialMethodLvSelector.java b/sootup.qilin/src/main/java/qilin/parm/select/PartialMethodLvSelector.java new file mode 100644 index 00000000000..50bd3500759 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/parm/select/PartialMethodLvSelector.java @@ -0,0 +1,74 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.parm.select; + +import java.util.Set; +import qilin.core.context.Context; +import qilin.core.pag.AllocNode; +import qilin.core.pag.FieldValNode; +import qilin.core.pag.LocalVarNode; +import qilin.parm.ctxcons.CtxConstructor; +import sootup.core.model.SootMethod; + +public class PartialMethodLvSelector extends CtxSelector { + private final int k; + private final int hk; + // precision-critical methods selected by the method-level approaches, e.g., Zipper. + private final Set pcm; + + public PartialMethodLvSelector(int k, int hk, Set pcm) { + this.k = k; + this.hk = hk; + this.pcm = pcm; + } + + @Override + public Context select(SootMethod m, Context context) { + if (pcm.contains(m)) { + return contextTailor(context, k); + } else { + return CtxConstructor.emptyContext; + } + } + + @Override + public Context select(LocalVarNode lvn, Context context) { + SootMethod sm = lvn.getMethod(); + if (pcm.contains(sm)) { + return contextTailor(context, k); + } else { + return CtxConstructor.emptyContext; + } + } + + @Override + public Context select(FieldValNode fvn, Context context) { + return contextTailor(context, k); + } + + @Override + public Context select(AllocNode heap, Context context) { + SootMethod sm = heap.getMethod(); + if (pcm.contains(sm)) { + return contextTailor(context, hk); + } else { + return context; + } + } +} diff --git a/sootup.qilin/src/main/java/qilin/parm/select/PartialVarSelector.java b/sootup.qilin/src/main/java/qilin/parm/select/PartialVarSelector.java new file mode 100644 index 00000000000..b95b6f565b5 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/parm/select/PartialVarSelector.java @@ -0,0 +1,86 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.parm.select; + +import java.util.Set; +import qilin.core.context.Context; +import qilin.core.pag.AllocNode; +import qilin.core.pag.Field; +import qilin.core.pag.FieldValNode; +import qilin.core.pag.LocalVarNode; +import qilin.parm.ctxcons.CtxConstructor; +import sootup.core.model.SootMethod; + +public class PartialVarSelector extends CtxSelector { + private final int k; + private final int hk; + // precision-critical nodes selected by the partial-variable-level approaches, e.g., Eagle, + // Turner. + private final Set csnodes; + private final Set pcm; + + public PartialVarSelector(int k, int hk, Set csnodes, Set pcm) { + this.k = k; + this.hk = hk; + this.csnodes = csnodes; + this.pcm = pcm; + } + + @Override + public Context select(SootMethod m, Context context) { + if (pcm.contains(m)) { + return contextTailor(context, k); + } else { + return CtxConstructor.emptyContext; + } + } + + @Override + public Context select(LocalVarNode lvn, Context context) { + Object ir = lvn.getVariable(); + if (csnodes.contains(ir)) { + return contextTailor(context, k); + } else { + return CtxConstructor.emptyContext; + } + } + + @Override + public Context select(FieldValNode fvn, Context context) { + Object tmp = fvn.getField(); + if (tmp instanceof Field) { + tmp = ((Field) tmp).getField(); + } + if (csnodes.contains(tmp)) { + return contextTailor(context, k); + } else { + return CtxConstructor.emptyContext; + } + } + + @Override + public Context select(AllocNode heap, Context context) { + Object ir = heap.getNewExpr(); + if (csnodes.contains(ir)) { + return contextTailor(context, hk); + } else { + return CtxConstructor.emptyContext; + } + } +} diff --git a/sootup.qilin/src/main/java/qilin/parm/select/PipelineSelector.java b/sootup.qilin/src/main/java/qilin/parm/select/PipelineSelector.java new file mode 100644 index 00000000000..f209e140817 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/parm/select/PipelineSelector.java @@ -0,0 +1,58 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.parm.select; + +import qilin.core.context.Context; +import qilin.core.pag.AllocNode; +import qilin.core.pag.FieldValNode; +import qilin.core.pag.LocalVarNode; +import sootup.core.model.SootMethod; + +/* + * A pipeline context selector which selects context by firstly using ctxSelA and then ctxSelB. + * */ +public class PipelineSelector extends CtxSelector { + private final CtxSelector ctxSelA; + private final CtxSelector ctxSelB; + + public PipelineSelector(CtxSelector lenSelA, CtxSelector lenSelB) { + this.ctxSelA = lenSelA; + this.ctxSelB = lenSelB; + } + + @Override + public Context select(SootMethod m, Context context) { + return ctxSelB.select(m, ctxSelA.select(m, context)); + } + + @Override + public Context select(LocalVarNode lvn, Context context) { + return ctxSelA.select(lvn, ctxSelB.select(lvn, context)); + } + + @Override + public Context select(FieldValNode fvn, Context context) { + return ctxSelA.select(fvn, ctxSelB.select(fvn, context)); + } + + @Override + public Context select(AllocNode heap, Context context) { + return ctxSelA.select(heap, ctxSelB.select(heap, context)); + } +} diff --git a/sootup.qilin/src/main/java/qilin/parm/select/UniformSelector.java b/sootup.qilin/src/main/java/qilin/parm/select/UniformSelector.java new file mode 100644 index 00000000000..f910c7c75d0 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/parm/select/UniformSelector.java @@ -0,0 +1,55 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.parm.select; + +import qilin.core.context.Context; +import qilin.core.pag.AllocNode; +import qilin.core.pag.FieldValNode; +import qilin.core.pag.LocalVarNode; +import sootup.core.model.SootMethod; + +public class UniformSelector extends CtxSelector { + private final int k; + private final int hk; + + public UniformSelector(int k, int hk) { + this.k = k; + this.hk = hk; + } + + @Override + public Context select(SootMethod m, Context context) { + return contextTailor(context, k); + } + + @Override + public Context select(LocalVarNode lvn, Context context) { + return contextTailor(context, k); + } + + @Override + public Context select(FieldValNode fvn, Context context) { + return contextTailor(context, k); + } + + @Override + public Context select(AllocNode heap, Context context) { + return contextTailor(context, hk); + } +} diff --git a/sootup.qilin/src/main/java/qilin/parm/select/VarLvSelector.java b/sootup.qilin/src/main/java/qilin/parm/select/VarLvSelector.java new file mode 100644 index 00000000000..86903dbdb1a --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/parm/select/VarLvSelector.java @@ -0,0 +1,69 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.parm.select; + +import java.util.Map; +import qilin.core.context.Context; +import qilin.core.pag.AllocNode; +import qilin.core.pag.FieldValNode; +import qilin.core.pag.LocalVarNode; +import sootup.core.model.SootMethod; + +/* + * This class is for a future technique and thus currently has no usage. + * */ +public class VarLvSelector extends CtxSelector { + private final int k; + private final int hk; + /* mapping from nodes to the context length they require. + * This is designed for a future approaches. + */ + private final Map node2Len; + private final Map mthd2Len; + + public VarLvSelector( + int k, int hk, Map node2Len, Map m2len) { + this.k = k; + this.hk = hk; + this.node2Len = node2Len; + this.mthd2Len = m2len; + } + + @Override + public Context select(SootMethod m, Context context) { + return contextTailor(context, Math.min(k, mthd2Len.getOrDefault(m, 0))); + } + + @Override + public Context select(LocalVarNode lvn, Context context) { + Object ir = lvn.getVariable(); + return contextTailor(context, Math.min(k, node2Len.getOrDefault(ir, 0))); + } + + @Override + public Context select(FieldValNode fvn, Context context) { + return contextTailor(context, Math.min(k, node2Len.getOrDefault(fvn.getField(), 0))); + } + + @Override + public Context select(AllocNode heap, Context context) { + Object ir = heap.getNewExpr(); + return contextTailor(context, Math.min(hk, node2Len.getOrDefault(ir, 0))); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/PTAConfig.java b/sootup.qilin/src/main/java/qilin/pta/PTAConfig.java new file mode 100644 index 00000000000..b70661dcf3c --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/PTAConfig.java @@ -0,0 +1,74 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021 Dongjie He + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta; + +import qilin.CoreConfig; +import qilin.driver.PTAPattern; +import qilin.pta.tools.DebloatedPTA; + +public class PTAConfig extends CoreConfig { + private static PTAConfig config = null; + + public static PTAConfig v() { + if (config == null) { + config = new PTAConfig(); + coreConfig = config; + } + return config; + } + + public static void reset() { + CoreConfig.reset(); + config = null; + } + + public static class PointerAnalysisConfiguration extends CorePTAConfiguration { + public PTAPattern ptaPattern; + + /** If this option is turned on, then main analysis will not run. */ + public boolean preAnalysisOnly = false; + + /** If this option is turned on, we will apply context debloating techniques. */ + public boolean ctxDebloating = false; + + public DebloatedPTA.DebloatApproach debloatApproach = DebloatedPTA.DebloatApproach.CONCH; + } + + /* + * Notice that the DEFAULT option is equivalent to Full Turner. + * PHASE_TWO: all objects are assumed to be CS-likely. + * PHASE_ONE: only non-CS-likely objects are analyzed context-insensitively. All other variables and objects + * are analyzed context-sensitively. + * */ + public enum TurnerConfig { + DEFAULT, + PHASE_TWO, + PHASE_ONE + } + + public TurnerConfig turnerConfig = TurnerConfig.DEFAULT; + + private PTAConfig() { + this.ptaConfig = new PointerAnalysisConfiguration(); + } + + public PointerAnalysisConfiguration getPtaConfig() { + return (PointerAnalysisConfiguration) this.ptaConfig; + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/bean/Bean.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/bean/Bean.java new file mode 100644 index 00000000000..5d743a2af8b --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/bean/Bean.java @@ -0,0 +1,92 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.bean; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import qilin.core.PTA; +import qilin.core.context.ContextElements; +import qilin.core.pag.AllocNode; +import qilin.pta.toolkits.common.OAG; +import qilin.util.ANSIColor; +import qilin.util.Pair; +import qilin.util.Stopwatch; + +public class Bean { + public static void run(PTA pta, Map>> beanNexCtxMap) { + System.out.println("Constructing object allocation graph (OAG) ..."); + Stopwatch timer = Stopwatch.newAndStart("OAG construction"); + OAG oag = new OAG(pta); + oag.build(); + timer.stop(); + System.out.print( + ANSIColor.BOLD + + "OAG construction: " + + ANSIColor.GREEN + + String.format("%.2fs", timer.elapsed()) + + ANSIColor.RESET + + "\n"); + + System.out.println("Computing contexts..."); + timer.restart(); + // The depth indicates the depth of heap context. + // The method context has 1 more level than heap context. + // Here depth is 1 which corresponds to 2-object-sensitive analysis + // with 1 heap context. + ContextSelector cs = new RepresentativeContextSelector(oag, 1); + timer.stop(); + System.out.print( + ANSIColor.BOLD + + "Context computation: " + + ANSIColor.GREEN + + String.format("%.2fs", timer.elapsed()) + + ANSIColor.RESET + + "\n"); + + writeContext(cs, oag, beanNexCtxMap); + } + + /* + * Should be generalized for k >= 3. + * */ + private static void writeContext( + ContextSelector cs, OAG oag, Map>> beanNexCtxMap) { + oag.allNodes() + .forEach( + allocator -> { + Set ctxs = cs.contextsOf(allocator); + for (ContextElements ctx : ctxs) { + AllocNode allocHctx = (AllocNode) ctx.get(0); + Set> csheaps = cs.allocatedBy(ctx, allocator); + if (csheaps != null) { + csheaps.forEach( + csheap -> { + AllocNode newHctx = (AllocNode) csheap.getFirst().get(0); + AllocNode heap = csheap.getSecond(); + beanNexCtxMap + .computeIfAbsent(heap.getNewExpr(), k -> new HashMap<>()) + .computeIfAbsent(allocator.getNewExpr(), k -> new HashMap<>()) + .put(allocHctx.getNewExpr(), newHctx.getNewExpr()); + }); + } + } + }); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/bean/ContextSelector.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/bean/ContextSelector.java new file mode 100644 index 00000000000..f9223e10ce3 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/bean/ContextSelector.java @@ -0,0 +1,63 @@ +/* Bean - Making k-Object-Sensitive Pointer Analysis More Precise with Still k-Limiting + * + * Copyright (C) 2016 Tian Tan, Yue Li, Jingling Xue + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package qilin.pta.toolkits.bean; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import qilin.core.context.ContextElements; +import qilin.core.pag.AllocNode; +import qilin.pta.toolkits.common.OAG; +import qilin.util.Pair; + +/** Compute the contexts of each heap object via OAG. */ +public abstract class ContextSelector { + + protected int depth; // the depth of context + protected Map> contextMap; + protected Map, Set>> + allocation = new HashMap<>(); + + protected ContextSelector(OAG oag) { + this(oag, Integer.MAX_VALUE); + } + + protected ContextSelector(OAG oag, int depth) { + this.depth = depth; + selectContext(oag); + } + + public Set contextsOf(AllocNode heap) { + return contextMap.get(heap); + } + + public Set> allocatedBy(ContextElements ctx, AllocNode heap) { + return allocation.get(new Pair<>(ctx, heap)); + } + + protected abstract void selectContext(OAG oag); + + protected void addAllocation( + ContextElements ctx, AllocNode heap, ContextElements newCtx, AllocNode succ) { + Pair csheap = new Pair<>(ctx, heap); + allocation.computeIfAbsent(csheap, k -> new HashSet<>()); + allocation.get(csheap).add(new Pair<>(newCtx, succ)); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/bean/RepresentativeContextSelector.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/bean/RepresentativeContextSelector.java new file mode 100644 index 00000000000..bca3654bd96 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/bean/RepresentativeContextSelector.java @@ -0,0 +1,129 @@ +/* Bean - Making k-Object-Sensitive Pointer Analysis More Precise with Still k-Limiting + * + * Copyright (C) 2016 Tian Tan, Yue Li, Jingling Xue + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package qilin.pta.toolkits.bean; + +import java.util.*; +import java.util.stream.Collectors; +import qilin.core.context.ContextElements; +import qilin.core.pag.AllocNode; +import qilin.parm.ctxcons.CtxConstructor; +import qilin.pta.toolkits.common.OAG; +import qilin.util.Triple; + +/** + * Context selector of BEAN. This selector aims to avoid redundant context elements while selecting + * contexts. + */ +public class RepresentativeContextSelector extends ContextSelector { + + public RepresentativeContextSelector(OAG oag, int depth) { + super(oag, depth); + } + + @Override + protected void selectContext(OAG oag) { + contextMap = new HashMap<>(); + oag.tailNodes() + .forEach( + node -> { + mergeContextMap(selectContext(oag, node)); + }); + oag.allNodes() + .forEach( + node -> { + if (!contextMap.containsKey(node)) { + mergeContextMap(selectContext(oag, node)); + } + }); + } + + private Map> selectContext(OAG oag, AllocNode dest) { + Map> tempContextMap = new HashMap<>(); + Queue> worklist = new LinkedList<>(); + initialWorkList(worklist, oag, dest); + while (!worklist.isEmpty()) { + Triple triple = worklist.poll(); + AllocNode heap = triple.getFirst(); + ContextElements ctx = triple.getSecond(); + boolean split = triple.getThird(); + if (!tempContextMap.containsKey(heap)) { + tempContextMap.put(heap, new HashSet<>()); + } + if (tempContextMap.get(heap).add(ctx)) { + Set reachSuccs = selectReachNodes(oag.getSuccsOf(heap), dest, oag); + final boolean isFork = reachSuccs.size() > 1; + reachSuccs.forEach( + succ -> { + final boolean isJoinSucc = oag.getInDegreeOf(succ) > 1; + ContextElements newCtx; + boolean succSplit = split; + if (split && isJoinSucc && !ctx.contains(heap)) { + newCtx = ContextElements.newContext(ctx, heap, depth); + succSplit = false; + } else { + newCtx = ctx; + } + if (isFork) { + succSplit = true; + } + Set ctxs = tempContextMap.get(succ); + if (ctxs == null || !ctxs.contains(newCtx)) { + addAllocation(ctx, heap, newCtx, succ); + worklist.add(new Triple<>(succ, newCtx, succSplit)); + } + }); + } + } + return tempContextMap; + } + + private void mergeContextMap(Map> anoContextMap) { + anoContextMap.forEach( + (heap, ctxs) -> { + if (contextMap.containsKey(heap)) { + contextMap.get(heap).addAll(ctxs); + } else { + contextMap.put(heap, ctxs); + } + }); + } + + /** + * Select the nodes from a node set which can reach the destination node + * + * @param nodes + * @param dest + * @param oag + * @return + */ + private Set selectReachNodes(Collection nodes, AllocNode dest, OAG oag) { + return nodes.stream().filter(node -> oag.reaches(node, dest)).collect(Collectors.toSet()); + } + + private void initialWorkList( + Queue> worklist, OAG oag, AllocNode node) { + Set reachRoots = selectReachNodes(oag.rootNodes(), node, oag); + boolean split = reachRoots.size() > 1; + ContextElements emptyCtx = (ContextElements) CtxConstructor.emptyContext; + reachRoots.forEach( + root -> + worklist.add( + new Triple<>(root, ContextElements.newContext(emptyCtx, root, depth), split))); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/bean/gpl-3.0.txt b/sootup.qilin/src/main/java/qilin/pta/toolkits/bean/gpl-3.0.txt new file mode 100644 index 00000000000..f288702d2fa --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/bean/gpl-3.0.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/common/DebloatedOAG.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/common/DebloatedOAG.java new file mode 100644 index 00000000000..70675b8ec51 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/common/DebloatedOAG.java @@ -0,0 +1,26 @@ +package qilin.pta.toolkits.common; + +import java.util.HashSet; +import java.util.Set; +import qilin.core.PTA; +import qilin.core.pag.AllocNode; + +public class DebloatedOAG extends OAG { + private final Set ctxDepHeaps; + + public DebloatedOAG(PTA prePta, Set ctxDepHeaps) { + super(prePta); + this.ctxDepHeaps = ctxDepHeaps; + } + + @Override + /** Add a directed object allocation edge to the OAG. */ + protected void addEdge(AllocNode src, AllocNode tgt) { + nodes.add(src); + nodes.add(tgt); + if (this.ctxDepHeaps.contains(tgt)) { + this.predecessors.computeIfAbsent(tgt, k -> new HashSet<>()).add(src); + this.successors.computeIfAbsent(src, k -> new HashSet<>()).add(tgt); + } + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/common/FieldPointstoGraph.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/common/FieldPointstoGraph.java new file mode 100644 index 00000000000..09b0072f85c --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/common/FieldPointstoGraph.java @@ -0,0 +1,105 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.common; + +import java.util.*; +import qilin.core.PTA; +import qilin.core.pag.AllocNode; +import qilin.core.pag.PAG; +import qilin.core.pag.SparkField; +import qilin.core.sets.PointsToSet; + +public class FieldPointstoGraph { + private final Map>> pointsTo = new HashMap<>(); + private final Map>> pointedBy = new HashMap<>(); + + public FieldPointstoGraph(PTA pta) { + buildFPG(pta); + } + + private void buildFPG(PTA pta) { + PAG pag = pta.getPag(); + pag.getAllocNodes().forEach(this::insertObj); + pag.getContextFields() + .forEach( + contextField -> { + AllocNode base = contextField.getBase(); + if (base.getMethod() == null) { + return; + } + SparkField field = contextField.getField(); + PointsToSet pts = pta.reachingObjects(contextField).toCIPointsToSet(); + for (Iterator it = pts.iterator(); it.hasNext(); ) { + AllocNode n = it.next(); + insertFPT(base, field, n); + } + }); + } + + public Set getAllObjs() { + return pointsTo.keySet(); + } + + public Set outFieldsOf(AllocNode baseObj) { + return pointsTo.getOrDefault(baseObj, Collections.emptyMap()).keySet(); + } + + public Set inFieldsOf(AllocNode obj) { + return pointedBy.get(obj).keySet(); + } + + public Set pointsTo(AllocNode baseObj, SparkField field) { + return pointsTo.get(baseObj).get(field); + } + + public Set pointedBy(AllocNode obj, SparkField field) { + return pointedBy.get(obj).get(field); + } + + public boolean hasFieldPointer(AllocNode obj, SparkField field) { + return pointsTo.get(obj).containsKey(field); + } + + private void insertObj(AllocNode obj) { + pointsTo.computeIfAbsent(obj, k -> new HashMap<>()); + pointedBy.computeIfAbsent(obj, k -> new HashMap<>()); + } + + /** + * Insert field points-to relation. + * + * @param baseObj the base object + * @param field a field of `baseObj' + * @param obj the object pointed by `field' + */ + private void insertFPT(AllocNode baseObj, SparkField field, AllocNode obj) { + insertPointsTo(baseObj, field, obj); + insertPointedBy(baseObj, field, obj); + } + + private void insertPointsTo(AllocNode baseObj, SparkField field, AllocNode obj) { + Map> fpt = pointsTo.computeIfAbsent(baseObj, k -> new HashMap<>()); + fpt.computeIfAbsent(field, k -> new HashSet<>()).add(obj); + } + + private void insertPointedBy(AllocNode baseObj, SparkField field, AllocNode obj) { + Map> fpb = pointedBy.computeIfAbsent(obj, k -> new HashMap<>()); + fpb.computeIfAbsent(field, k -> new HashSet<>()).add(baseObj); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/common/OAG.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/common/OAG.java new file mode 100644 index 00000000000..78f2582970a --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/common/OAG.java @@ -0,0 +1,160 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.common; + +import java.util.*; +import qilin.core.PTA; +import qilin.core.builder.MethodNodeFactory; +import qilin.core.pag.AllocNode; +import qilin.core.pag.LocalVarNode; +import qilin.core.pag.MethodPAG; +import qilin.core.sets.PointsToSet; +import qilin.util.PTAUtils; +import qilin.util.graph.DirectedGraph; +import qilin.util.queue.QueueReader; +import sootup.core.model.SootMethod; + +/** Implementation of Object Allocation Graph (OAG). */ +public class OAG implements DirectedGraph { + protected final PTA pta; + protected final Map> successors; + protected final Map> predecessors; + protected final Set nodes = new HashSet<>(); + protected Collection rootNodes; + protected Collection tailNodes; + + public OAG(PTA prePta) { + this.pta = prePta; + this.predecessors = new HashMap<>(); + this.successors = new HashMap<>(); + } + + public void build() { + buildOAG(); + rootNodes = computeRootNodes(); + tailNodes = computeTailNodes(); + } + + @Override + public Collection allNodes() { + return nodes; + } + + @Override + public Collection predsOf(AllocNode p) { + return getPredsOf(p); + } + + @Override + public Collection succsOf(AllocNode p) { + return getSuccsOf(p); + } + + public Collection rootNodes() { + return rootNodes; + } + + public Collection tailNodes() { + return tailNodes; + } + + public Set getPredsOf(AllocNode n) { + return predecessors.getOrDefault(n, Collections.emptySet()); + } + + public Set getSuccsOf(AllocNode n) { + return successors.getOrDefault(n, Collections.emptySet()); + } + + public int getInDegreeOf(AllocNode n) { + return getPredsOf(n).size(); + } + + /** + * @param source + * @param dest + * @return whether there is a path from source to target in the OAG + */ + Map> reachableMap = new HashMap<>(); + + public boolean reaches(AllocNode source, AllocNode dest) { + Collection reachableNodes = reachableMap.get(source); + if (reachableNodes == null) { + reachableNodes = computeReachableNodes(source); + reachableMap.put(source, reachableNodes); + } + return reachableNodes.contains(dest); + } + + protected void buildOAG() { + Map> pts = PTAUtils.calcStaticThisPTS(this.pta); + for (SootMethod method : this.pta.getNakedReachableMethods()) { + if (!PTAUtils.hasBody(method)) { + continue; + } + MethodPAG srcmpag = pta.getPag().getMethodPAG(method); + MethodNodeFactory srcnf = srcmpag.nodeFactory(); + LocalVarNode thisRef = (LocalVarNode) srcnf.caseThis(); + QueueReader reader = srcmpag.getInternalReader().clone(); + while (reader.hasNext()) { + qilin.core.pag.Node from = reader.next(), to = reader.next(); + if (from instanceof AllocNode) { + AllocNode tgt = (AllocNode) from; + if (PTAUtils.isFakeMainMethod(method)) { + // special treatment for fake main + AllocNode src = pta.getRootNode(); + addEdge(src, tgt); + } else if (method.isStatic()) { + pts.getOrDefault(thisRef, Collections.emptySet()) + .forEach( + src -> { + addEdge(src, tgt); + }); + } else { + PointsToSet thisPts = pta.reachingObjects(thisRef).toCIPointsToSet(); + for (Iterator it = thisPts.iterator(); it.hasNext(); ) { + AllocNode src = it.next(); + addEdge(src, tgt); + } + } + } + } + } + } + + /** Add a directed object allocation edge to the OAG. */ + protected void addEdge(AllocNode src, AllocNode tgt) { + nodes.add(src); + nodes.add(tgt); + this.predecessors.computeIfAbsent(tgt, k -> new HashSet<>()).add(src); + this.successors.computeIfAbsent(src, k -> new HashSet<>()).add(tgt); + } + + public int nodeSize() { + return this.nodes.size(); + } + + public int edgeSize() { + int ret = 0; + for (AllocNode obj : predecessors.keySet()) { + ret += predecessors.get(obj).size(); + } + return ret; + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/common/ToolUtil.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/common/ToolUtil.java new file mode 100644 index 00000000000..ec0b6fc8aae --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/common/ToolUtil.java @@ -0,0 +1,61 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.common; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import qilin.core.PTA; +import qilin.core.builder.MethodNodeFactory; +import qilin.core.pag.PAG; +import qilin.core.pag.VarNode; +import sootup.core.model.SootMethod; +import sootup.core.types.ReferenceType; + +public class ToolUtil { + public static VarNode getThis(PAG pag, SootMethod m) { + MethodNodeFactory mthdNF = pag.getMethodPAG(m).nodeFactory(); + return mthdNF.caseThis(); + } + + public static Set getParameters(PAG pag, SootMethod m) { + MethodNodeFactory mthdNF = pag.getMethodPAG(m).nodeFactory(); + Set ret = new HashSet<>(); + for (int i = 0; i < m.getParameterCount(); ++i) { + if (m.getParameterType(i) instanceof ReferenceType) { + qilin.core.pag.VarNode param = mthdNF.caseParm(i); + ret.add(param); + } + } + return ret; + } + + public static Set getRetVars(PAG pag, SootMethod m) { + MethodNodeFactory mthdNF = pag.getMethodPAG(m).nodeFactory(); + if (m.getReturnType() instanceof ReferenceType) { + VarNode ret = mthdNF.caseRet(); + return Collections.singleton(ret); + } + return Collections.emptySet(); + } + + public static int pointsToSetSizeOf(final PTA pta, VarNode var) { + return pta.reachingObjects(var).toCIPointsToSet().size(); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/AbstractConch.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/AbstractConch.java new file mode 100644 index 00000000000..16b32748e68 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/AbstractConch.java @@ -0,0 +1,237 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.conch; + +import java.util.*; +import qilin.core.PTA; +import qilin.core.builder.MethodNodeFactory; +import qilin.core.pag.*; +import qilin.core.sets.PointsToSet; +import qilin.util.PTAUtils; +import qilin.util.Pair; +import sootup.core.model.SootMethod; +import sootup.core.types.ArrayType; +import sootup.core.types.PrimitiveType; + +public class AbstractConch { + public final PTA pta; + public final PAG pag; + + protected final Map> invokedMethods = new HashMap<>(); + // field |--> + protected final Map>>> m2thisFStores = + new HashMap<>(); + protected final Map>>> o2nonThisFStores = + new HashMap<>(); + protected final Map>> m2thisFLoads = new HashMap<>(); + protected final Map>> o2nonThisFLoads = new HashMap<>(); + protected final Map> o2fs = new HashMap<>(); + + public AbstractConch(PTA pta) { + this.pta = pta; + this.pag = pta.getPag(); + init(); + } + + private void init() { + /* + * Static methods are modeled as a special instance methods of its most recent instance methods' receciver objects. + * thus, inherit its most recent instance methods' contexts (which is standard in the literature). + * The following line computes the receiver objects for the this_ptr of static methods. + * */ + Map> pts = PTAUtils.calcStaticThisPTS(pta); + pta.getNakedReachableMethods().stream() + .filter(PTAUtils::hasBody) + .forEach( + method -> { + collectStoresIn(method); + collectLoadsIn(method); + buildInvokedOnFor(method, pts); + }); + + buildHeap2AccessedFieldsMap(); + } + + /* + * build a map, heap |--> {fields}. + * */ + private void buildHeap2AccessedFieldsMap() { + for (AllocNode heap : pag.getAllocNodes()) { + Set tmp = new HashSet<>(); + tmp.addAll(o2nonThisFLoads.getOrDefault(heap, Collections.emptyMap()).keySet()); + tmp.addAll(o2nonThisFStores.getOrDefault(heap, Collections.emptyMap()).keySet()); + for (SootMethod sm : invokedMethods.getOrDefault(heap, Collections.emptySet())) { + tmp.addAll(m2thisFLoads.getOrDefault(sm, Collections.emptyMap()).keySet()); + tmp.addAll(m2thisFStores.getOrDefault(sm, Collections.emptyMap()).keySet()); + } + o2fs.put(heap, tmp); + } + } + + /* + * give a method, we map its receiver objects to this method. + * */ + private void buildInvokedOnFor(SootMethod m, Map> pts) { + MethodPAG srcmpag = pag.getMethodPAG(m); + MethodNodeFactory srcnf = srcmpag.nodeFactory(); + LocalVarNode thisRef = (LocalVarNode) srcnf.caseThis(); + + if (m.isStatic()) { + pts.getOrDefault(thisRef, Collections.emptySet()) + .forEach( + a -> { + invokedMethods.computeIfAbsent(a, k -> new HashSet<>()).add(m); + }); + } else { + PointsToSet thisPts = pta.reachingObjects(thisRef).toCIPointsToSet(); + for (Iterator it = thisPts.iterator(); it.hasNext(); ) { + AllocNode n = it.next(); + invokedMethods.computeIfAbsent(n, k -> new HashSet<>()).add(m); + } + } + } + + private final Map methodSMPAGMap = new HashMap<>(); + + public SMPAG getSMAPG(MethodPAG mpag) { + return methodSMPAGMap.computeIfAbsent(mpag, k -> new SMPAG(mpag)); + } + + private void collectLoadsIn(SootMethod method) { + MethodPAG srcmpag = pag.getMethodPAG(method); + MethodNodeFactory srcnf = srcmpag.nodeFactory(); + LocalVarNode thisRef = (LocalVarNode) srcnf.caseThis(); + SMPAG smpag = getSMAPG(srcmpag); + for (Pair ld : smpag.getLoads()) { + FieldRefNode fr = (FieldRefNode) ld.getSecond(); + LocalVarNode loadBase = (LocalVarNode) fr.getBase(); + SparkField field = fr.getField(); + if (primitiveField(field)) { + continue; + } + if (PTAUtils.mustAlias(pta, thisRef, loadBase)) { // handle THIS LOAD, i.e., ... = this.f + Map> f2bs = + m2thisFLoads.computeIfAbsent(method, k -> new HashMap<>()); + f2bs.computeIfAbsent(field, k -> new HashSet<>()).add(loadBase); + } else { + for (AllocNode heap : pta.reachingObjects(loadBase).toCIPointsToSet().toCollection()) { + if (heap.getMethod() != method) { + /* we filter loads in the containing method, + * since this often not satisfy the second theoretical condition O.f*--)-->v. + */ + Map> f2bs = + o2nonThisFLoads.computeIfAbsent(heap, k -> new HashMap<>()); + f2bs.computeIfAbsent(field, k -> new HashSet<>()).add(loadBase); + } + } + } + } + } + + private void collectStoresIn(SootMethod method) { + MethodPAG srcmpag = pag.getMethodPAG(method); + MethodNodeFactory srcnf = srcmpag.nodeFactory(); + LocalVarNode thisRef = (LocalVarNode) srcnf.caseThis(); + SMPAG smpag = getSMAPG(srcmpag); + for (Pair st : smpag.getStores()) { + LocalVarNode from = (LocalVarNode) st.getSecond(); + FieldRefNode fr = (FieldRefNode) st.getFirst(); + LocalVarNode storeBase = (LocalVarNode) fr.getBase(); + SparkField field = fr.getField(); + if (primitiveField(field)) { + continue; + } + if (PTAUtils.mustAlias(pta, thisRef, storeBase)) { // handle this STORE, i.e., this.f = ... + Map>> m2s = + m2thisFStores.computeIfAbsent(method, k -> new HashMap<>()); + m2s.computeIfAbsent(field, k -> new HashSet<>()).add(new Pair<>(storeBase, from)); + } else { + for (AllocNode heap : pta.reachingObjects(storeBase).toCIPointsToSet().toCollection()) { + if (!emptyFieldPts(heap, field)) { + Map>> f2bs = + o2nonThisFStores.computeIfAbsent(heap, k -> new HashMap<>()); + f2bs.computeIfAbsent(field, k -> new HashSet<>()).add(new Pair<>(storeBase, from)); + } + } + } + } + } + + private boolean primitiveField(SparkField f) { + String s = "java.lang.String"; + if (f.getType() instanceof PrimitiveType) { + return true; + } else if (f.getType() instanceof ArrayType) { + ArrayType at = (ArrayType) f.getType(); + /* + * here, we let primitive array as primitive type as that in Turner. + * this wont hurt precision of clients. + * */ + return at.getBaseType() instanceof PrimitiveType; + } else return f.getType().toString().equals(s); + } + + protected boolean emptyFieldPts(AllocNode heap, SparkField field) { + PointsToSet pts = pta.reachingObjectsInternal(heap, field); + Set tmp = new HashSet<>(); + for (Iterator it = pts.iterator(); it.hasNext(); ) { + AllocNode n = it.next(); + // filter StringConstant. + if (!(n instanceof StringConstantNode)) { + tmp.add(n); + } + } + return tmp.isEmpty(); + } + + protected boolean hasLoadOn(AllocNode heap, SparkField field) { + Map> f2bs = o2nonThisFLoads.getOrDefault(heap, Collections.emptyMap()); + Set loadBases = f2bs.getOrDefault(field, Collections.emptySet()); + if (!loadBases.isEmpty()) { + return true; + } + for (SootMethod method : invokedMethods.getOrDefault(heap, Collections.emptySet())) { + Map> f2bsX = + m2thisFLoads.getOrDefault(method, Collections.emptyMap()); + Set loadBasesX = f2bsX.getOrDefault(field, Collections.emptySet()); + if (!loadBasesX.isEmpty()) { + return true; + } + } + return false; + } + + protected boolean hasStoreOn(AllocNode heap, SparkField field) { + Map>> f2bs = + o2nonThisFStores.getOrDefault(heap, Collections.emptyMap()); + Set> storeBases = f2bs.getOrDefault(field, Collections.emptySet()); + if (!storeBases.isEmpty()) { + return true; + } + for (SootMethod method : invokedMethods.getOrDefault(heap, Collections.emptySet())) { + Map>> f2bsX = + m2thisFStores.getOrDefault(method, Collections.emptyMap()); + Set> storeBasesX = f2bsX.getOrDefault(field, Collections.emptySet()); + if (!storeBasesX.isEmpty()) { + return true; + } + } + return false; + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/AbstractPAG.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/AbstractPAG.java new file mode 100644 index 00000000000..c9d190fca60 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/AbstractPAG.java @@ -0,0 +1,234 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.conch; + +import com.google.common.collect.Streams; +import heros.solver.CountingThreadPoolExecutor; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import qilin.core.PTA; +import qilin.core.PointsToAnalysis; +import qilin.core.builder.MethodNodeFactory; +import qilin.core.builder.callgraph.OnFlyCallGraph; +import qilin.core.pag.*; +import qilin.util.PTAUtils; +import qilin.util.queue.QueueReader; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.SootMethod; +import sootup.core.types.ReferenceType; + +/* + * The abstract pointer assignment graph constructed by rules in Fig. 10 and Fig. 11 in our paper. + * our IFDS analysis is running on PAG rather than the conventional control flow graph. + * */ +public abstract class AbstractPAG { + /* + * A symbolic object is introduced to abstract all the possible objects returned from a callsite. + * */ + protected final Map> symbolicHeaps = new ConcurrentHashMap<>(); + protected final Map> outEdges = new ConcurrentHashMap<>(); + protected final Map> sumEdges = new ConcurrentHashMap<>(); + + protected CountingThreadPoolExecutor executor; + + protected final PTA prePTA; + protected final PAG prePAG; + protected final OnFlyCallGraph callGraph; + + protected AbstractPAG(PTA prePTA) { + this.prePTA = prePTA; + this.prePAG = prePTA.getPag(); + this.callGraph = prePTA.getCallGraph(); + int threadNum = Runtime.getRuntime().availableProcessors(); + this.executor = + new CountingThreadPoolExecutor( + threadNum, Integer.MAX_VALUE, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); + } + + protected void build() { + prePTA + .getNakedReachableMethods() + .parallelStream() + .filter(PTAUtils::hasBody) + .forEach(this::buildFG); + } + + private void buildFG(SootMethod method) { + MethodPAG srcmpag = prePAG.getMethodPAG(method); + MethodNodeFactory srcnf = srcmpag.nodeFactory(); + LocalVarNode thisRef = (LocalVarNode) srcnf.caseThis(); + QueueReader reader = srcmpag.getInternalReader().clone(); + while (reader.hasNext()) { + Node from = reader.next(), to = reader.next(); + if (from instanceof LocalVarNode) { + if (to instanceof LocalVarNode) this.addAssignEdge((LocalVarNode) from, (LocalVarNode) to); + else if (to instanceof FieldRefNode) { + FieldRefNode fr = (FieldRefNode) to; + this.addStoreEdge((LocalVarNode) from, (LocalVarNode) fr.getBase()); + } // local-global + + } else if (from instanceof AllocNode) { + if (to instanceof LocalVarNode) { + this.addNewEdge((AllocNode) from, (LocalVarNode) to); + } // GlobalVarNode + } else if (from instanceof FieldRefNode) { + FieldRefNode fr = (FieldRefNode) from; + this.addLoadEdge((LocalVarNode) fr.getBase(), (LocalVarNode) to); + } // global-local + } + + // add param and return edges + addParamEdge(thisRef); + + int numParms = method.getParameterCount(); + for (int i = 0; i < numParms; i++) { + if (method.getParameterType(i) instanceof ReferenceType) { + LocalVarNode param = (LocalVarNode) srcnf.caseParm(i); + addParamEdge(param); + } + } + if (method.getReturnType() instanceof ReferenceType) { + LocalVarNode mret = (LocalVarNode) srcnf.caseRet(); + addReturnEdge(mret); + } + Node throwNode = + prePAG.findLocalVarNode( + method, + new Parm(method, PointsToAnalysis.THROW_NODE), + PTAUtils.getClassType("java.lang.Throwable")); + if (throwNode != null) { + addThrowEdge(throwNode); + } + } + + protected void addNormalEdge(TranEdge edge) { + outEdges.computeIfAbsent(edge.getSource(), k -> ConcurrentHashMap.newKeySet()).add(edge); + } + + protected void addThrowEdge(Node throwNode) { + addNormalEdge(new TranEdge(throwNode, throwNode, DFA.TranCond.THROW)); + addNormalEdge(new TranEdge(throwNode, throwNode, DFA.TranCond.I_THROW)); + } + + protected void addParamEdge(LocalVarNode param) { + addNormalEdge(new TranEdge(param, param, DFA.TranCond.PARAM)); + addNormalEdge(new TranEdge(param, param, DFA.TranCond.I_PARAM)); + } + + protected void addReturnEdge(LocalVarNode mret) { + addNormalEdge(new TranEdge(mret, mret, DFA.TranCond.RETURN)); + addNormalEdge(new TranEdge(mret, mret, DFA.TranCond.I_RETURN)); + } + + protected void addNewEdge(AllocNode from, LocalVarNode to) { + // skip merged heaps. + if (from.getMethod() == null && !(from instanceof ConstantNode)) { + return; + } + TranEdge newEdge = new TranEdge(from, to, DFA.TranCond.NEW); + addNormalEdge(newEdge); + TranEdge newInvEdge = new TranEdge(to, from, DFA.TranCond.I_NEW); + addNormalEdge(newInvEdge); + } + + protected void addAssignEdge(LocalVarNode from, LocalVarNode to) { + TranEdge assignEdge = new TranEdge(from, to, DFA.TranCond.ASSIGN); + addNormalEdge(assignEdge); + TranEdge assignInvEdge = new TranEdge(to, from, DFA.TranCond.I_ASSIGN); + addNormalEdge(assignInvEdge); + } + + protected void addStoreEdge(LocalVarNode from, LocalVarNode base) { + TranEdge storeEdge = new TranEdge(from, base, DFA.TranCond.STORE); + addNormalEdge(storeEdge); + TranEdge storeInvEdge = new TranEdge(base, from, DFA.TranCond.I_STORE); + addNormalEdge(storeInvEdge); + } + + protected void addLoadEdge(LocalVarNode base, LocalVarNode to) { + TranEdge loadEdge = new TranEdge(base, to, DFA.TranCond.LOAD); + addNormalEdge(loadEdge); + TranEdge loadInvEdge = new TranEdge(to, base, DFA.TranCond.I_LOAD); + addNormalEdge(loadInvEdge); + } + + /* + * The following code forms the bases of a multi-threaded IFDS solver. + * The codes is modified from the implementation of the fast IFDS solver in FlowDroid. + */ + protected void solve() { + submitInitialSeeds(); + awaitCompletionComputeValuesAndShutdown(); + } + + protected Collection outAndSummaryEdges(Node node) { + return Streams.concat( + outEdges.getOrDefault(node, Collections.emptySet()).stream(), + sumEdges.getOrDefault(node, Collections.emptySet()).stream()) + .collect(Collectors.toSet()); + } + + protected abstract void submitInitialSeeds(); + + protected void awaitCompletionComputeValuesAndShutdown() { + { + // run executor and await termination of tasks + runExecutorAndAwaitCompletion(); + } + // ask executor to shut down; + // this will cause new submissions to the executor to be rejected, + // but at this point all tasks should have completed anyway + executor.shutdown(); + + // Wait for the executor to be really gone + while (!executor.isTerminated()) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + // silently ignore the exception, it's not an issue if the + // thread gets aborted + } + } + executor = null; + } + + private void runExecutorAndAwaitCompletion() { + try { + executor.awaitCompletion(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + Throwable exception = executor.getException(); + if (exception != null) { + throw new RuntimeException("There were exceptions during IFDS analysis. Exiting.", exception); + } + } + + protected AllocNode getSymbolicHeapOf(SootMethod method, Stmt invokeStmt) { + return symbolicHeaps + .computeIfAbsent(method, k -> new ConcurrentHashMap<>()) + .computeIfAbsent(invokeStmt, k -> new AllocNode(invokeStmt, null, method)); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/CSDG.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/CSDG.java new file mode 100644 index 00000000000..632661d29a7 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/CSDG.java @@ -0,0 +1,62 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.conch; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import qilin.core.pag.AllocNode; +import qilin.util.graph.DirectedGraphImpl; + +/* + * context-sensitivity dependent graph: + * an edge l1 --> l2 means if l2 is context-insensitive, then l1 should be context-insensitive. + * if l2 is context-sensitive, then l1 also should be context-sensitive. + * if edges form a cycle, i.e, l1-->l2-->l3-->l1 and non nodes's context-sensitivity + * in this cycle could be decide yet, we assume all of them to be context-insensitive. + * To efficiently check Obs 3(b) (lines 18-23 in Algorithm 1), we introduce this graph structure. + * */ +public class CSDG extends DirectedGraphImpl { + /* + * Remove node on the graph that does not have successor. + * */ + public void removeNode(final AllocNode to) { + if (succs.containsKey(to) && !succs.get(to).isEmpty()) { + return; + } + succs.remove(to); + if (preds.containsKey(to)) { + for (AllocNode from : preds.get(to)) { + succs.get(from).remove(to); + } + preds.remove(to); + } + nodes.remove(to); + } + + public Set noOutDegreeNodes() { + Set ret = new HashSet<>(); + for (AllocNode node : allNodes()) { + if (succs.getOrDefault(node, Collections.emptySet()).isEmpty()) { + ret.add(node); + } + } + return ret; + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/Conch.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/Conch.java new file mode 100644 index 00000000000..43e4694746c --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/Conch.java @@ -0,0 +1,412 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.conch; + +import java.util.*; +import java.util.stream.Collectors; +import qilin.core.PTA; +import qilin.core.builder.MethodNodeFactory; +import qilin.core.pag.*; +import qilin.core.sets.PointsToSet; +import qilin.util.PTAUtils; +import qilin.util.Pair; +import sootup.core.jimple.basic.Value; +import sootup.core.jimple.common.expr.AbstractInvokeExpr; +import sootup.core.jimple.common.expr.JSpecialInvokeExpr; +import sootup.core.jimple.common.stmt.JInvokeStmt; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.SootMethod; +import sootup.core.signatures.MethodSignature; + +/* + * This classifier will classify heaps into context-dependent and context-independent heaps. + * */ +public class Conch extends AbstractConch { + + private final LeakAnalysis mfg; + private final DepOnParamAnalysis pfg; + + private final Set csHeaps = new HashSet<>(); + private final Set ciHeaps = new HashSet<>(); + + public Set ctxDependentHeaps() { + return csHeaps.stream().map(AllocNode::getNewExpr).collect(Collectors.toSet()); + } + + public Set ctxIndenpendentHeaps() { + return ciHeaps; + } + + public Set ctxDependentHeaps2() { + return csHeaps; + } + + public Conch(PTA pta) { + super(pta); + this.mfg = new LeakAnalysis(pta); + this.pfg = new DepOnParamAnalysis(pta); + } + + private SootMethod findInvokedConstructorOf(AllocNode heap) { + SootMethod containingMethod = heap.getMethod(); + MethodPAG cmpag = pag.getMethodPAG(containingMethod); + MethodNodeFactory nodeFactory = cmpag.nodeFactory(); + for (Stmt unit : cmpag.getInvokeStmts()) { + if (unit instanceof JInvokeStmt) { + JInvokeStmt invokeStmt = (JInvokeStmt) unit; + AbstractInvokeExpr expr = invokeStmt.getInvokeExpr(); + if (expr instanceof JSpecialInvokeExpr) { + JSpecialInvokeExpr iie = (JSpecialInvokeExpr) expr; + Value base = iie.getBase(); + VarNode baseNode = (VarNode) nodeFactory.getNode(base); + PointsToSet v1pts = pta.reachingObjects(baseNode); + SootMethod target = pta.getView().getMethod(iie.getMethodSignature()).get(); + if (v1pts.size() == 1 + && v1pts.toCIPointsToSet().contains(heap) + && PTAUtils.isConstructor(target)) { + return target; + } + } + } + } + return null; // not found. + } + + private SootMethod findInvokedConstructorOf(SootMethod outerInit) { + MethodPAG cmpag = pag.getMethodPAG(outerInit); + MethodNodeFactory nodeFactory = cmpag.nodeFactory(); + VarNode thisNode = nodeFactory.caseThis(); + for (Stmt unit : cmpag.getInvokeStmts()) { + if (unit instanceof JInvokeStmt) { + JInvokeStmt invokeStmt = (JInvokeStmt) unit; + AbstractInvokeExpr expr = invokeStmt.getInvokeExpr(); + if (expr instanceof JSpecialInvokeExpr) { + JSpecialInvokeExpr iie = (JSpecialInvokeExpr) expr; + Value base = iie.getBase(); + VarNode baseNode = (VarNode) nodeFactory.getNode(base); + MethodSignature targetSig = iie.getMethodSignature(); + SootMethod target = pta.getView().getMethod(targetSig).get(); + if (PTAUtils.mustAlias(pta, thisNode, baseNode) + && targetSig.getSubSignature().getName().equals("")) { + return target; + } + } + } + } + return null; // not found. + } + + private ArrayList recoverConstructorChain(SootMethod sm, AllocNode heap) { + ArrayList ret = new ArrayList<>(); + SootMethod origInit = findInvokedConstructorOf(heap); + if (origInit != null) { + while (origInit != sm) { + ret.add(0, origInit); + origInit = findInvokedConstructorOf(origInit); + if (origInit == null) { + break; + } + } + } + return ret; + } + + private Set mappingtoCallerCommingParamsOrHeaps( + Set params, SootMethod curr, SootMethod caller) { + MethodPAG cmpag = pag.getMethodPAG(caller); + Set ret = new HashSet<>(); + for (Stmt stmt : cmpag.getInvokeStmts()) { + if (!(stmt.getInvokeExpr() instanceof JSpecialInvokeExpr)) { + continue; + } + MethodSignature methodSig = stmt.getInvokeExpr().getMethodSignature(); + Optional otarget = pta.getView().getMethod(methodSig); + if (otarget.isPresent() && otarget.get().equals(curr)) { + for (Node n : params) { + if (n instanceof VarNode) { + VarNode paramNode = (VarNode) n; + LocalVarNode argNode = PTAUtils.paramToArg(pag, stmt, cmpag, paramNode); + if (argNode != null) { + ret.addAll(this.pfg.fetchReachableParamsOf(argNode)); + } + } + } + } + } + return ret; + } + + private boolean containHeaps(Set nodes) { + boolean ret = false; + for (Node n : nodes) { + if (n instanceof AllocNode) { + ret = true; + break; + } + } + return ret; + } + + private Trilean handleTransitiveConstructors(SootMethod sm, AllocNode heap, Set params) { + SootMethod containingMethod = heap.getMethod(); + ArrayList chain = recoverConstructorChain(sm, heap); + SootMethod caller = sm; + SootMethod curr; + Set ret = params; + boolean notSure = containHeaps(params); + for (SootMethod method : chain) { + curr = caller; + caller = method; + ret = mappingtoCallerCommingParamsOrHeaps(ret, curr, caller); + notSure |= containHeaps(ret); + Trilean res = checkResult(ret); + if (res != Trilean.TRUE) { + if (notSure) { + return Trilean.UNKNOWN; + } else { + return Trilean.FALSE; + } + } + } + curr = caller; + caller = containingMethod; + ret = mappingtoCallerCommingParamsOrHeaps(ret, curr, caller); + Trilean tmpRes2 = checkResult(ret); + if (notSure) { + tmpRes2 = Trilean.OR(tmpRes2, Trilean.UNKNOWN); + } + return tmpRes2; + } + + private Trilean checkResult(Set res) { + if (res.isEmpty()) { + return Trilean.FALSE; + } + boolean hasParam = false; + for (Node n : res) { + if (!(n instanceof AllocNode)) { + hasParam = true; + break; + } + } + if (hasParam) { + return Trilean.TRUE; + } else { + return Trilean.UNKNOWN; + } + } + + private Trilean isCommingFromParams(LocalVarNode from, SootMethod method, AllocNode heap) { + Set ret = this.pfg.fetchReachableParamsOf(from); + if (PTAUtils.isConstructor(method)) { + return handleTransitiveConstructors(method, heap, ret); + } else { + return checkResult(ret); + } + } + + private final Map> notSureFields = new HashMap<>(); + + private Trilean checkHeap(AllocNode heap) { + Set fields = o2fs.getOrDefault(heap, Collections.emptySet()); + Trilean ret = Trilean.FALSE; + for (SparkField field : fields) { + Trilean csorci = Trilean.FALSE; + if (!hasLoadOn(heap, field) || !hasStoreOn(heap, field) || emptyFieldPts(heap, field)) { + continue; + } + + // check stores: + Map>> f2sts = + o2nonThisFStores.getOrDefault(heap, Collections.emptyMap()); + Set> pairs = f2sts.getOrDefault(field, Collections.emptySet()); + if (!pairs.isEmpty()) { + for (Pair pair : pairs) { + LocalVarNode storeBase = (LocalVarNode) pair.getFirst(); + VarNode from = pair.getSecond(); + if (storeBase.getMethod() != heap.getMethod()) { + csorci = Trilean.TRUE; + } else { + Trilean fromparam = + isCommingFromParams((LocalVarNode) from, storeBase.getMethod(), heap); + csorci = Trilean.OR(csorci, fromparam); + if (fromparam == Trilean.UNKNOWN) { + notSureFields.computeIfAbsent(heap, k -> new HashSet<>()).add(field); + } + } + } + } + Set onMethods = invokedMethods.getOrDefault(heap, Collections.emptySet()); + for (SootMethod method : onMethods) { + Map>> f2stsX = + m2thisFStores.getOrDefault(method, Collections.emptyMap()); + Set> thisFStores = + f2stsX.getOrDefault(field, Collections.emptySet()); + if (!thisFStores.isEmpty()) { + for (Pair pair : thisFStores) { + VarNode from = pair.getSecond(); + Trilean fromparam = isCommingFromParams((LocalVarNode) from, method, heap); + csorci = Trilean.OR(csorci, fromparam); + } + } + } + + ret = Trilean.OR(ret, csorci); + if (csorci == Trilean.UNKNOWN) { + notSureFields.computeIfAbsent(heap, k -> new HashSet<>()).add(field); + } + } + return ret; + } + + private boolean hasInstanceFieldWithStoreLoad(AllocNode heap) { + Set fields = o2fs.getOrDefault(heap, Collections.emptySet()); + for (SparkField field : fields) { + boolean hasLoads = hasLoadOn(heap, field); + boolean hasStores = hasStoreOn(heap, field); + boolean emptyFieldPts = emptyFieldPts(heap, field); + if (!hasLoads || !hasStores || emptyFieldPts) { + continue; + } + return true; + } + return false; + } + + public void runClassifier() { + Collection allHeaps = pag.getAllocNodes(); + int heapCnt = allHeaps.size(); + int[] condACnt = new int[1]; + + /* + * pre-process. + * Those heaps usually are assigned empty context in a tradition pointer analysis. + * Classify them to be CS or CI does not affect the efficiency of pointer analysis. + * Thus, we handle them in the pre-process. + */ + Set remainToSolve = new HashSet<>(); + allHeaps.forEach( + heap -> { + if (heap.getMethod() == null + || heap instanceof ConstantNode + || PTAUtils.isEmptyArray(heap) + || PTAUtils.isOfPrimitiveBaseType(heap)) { + ciHeaps.add(heap); + } else { + SootMethod mthd = heap.getMethod(); + if (PTAUtils.isStaticInitializer(mthd)) { + ciHeaps.add(heap); + } else { + remainToSolve.add(heap); + } + } + }); + + // check by rules. + Set unknownyet = new HashSet<>(); + remainToSolve.forEach( + heap -> { + // Obs 2 + boolean condA = this.mfg.isLeakObject(heap); + condACnt[0] += (condA ? 1 : 0); + if (!condA) { + ciHeaps.add(heap); + return; + } + // Obs 1 + boolean condB = hasInstanceFieldWithStoreLoad(heap); + if (!condB) { + ciHeaps.add(heap); + return; + } + // Obs 3 (a) + Trilean condExtra = checkHeap(heap); + if (condExtra == Trilean.TRUE) { + csHeaps.add(heap); + } else if (condExtra == Trilean.FALSE) { + ciHeaps.add(heap); + } else { + unknownyet.add(heap); + } + }); + // Obs 3 (b) + classifyForRemain(unknownyet); + // stat + System.out.println("#Heaps:" + heapCnt); + System.out.println("#CondA:" + condACnt[0]); + System.out.println("#CS:" + csHeaps.size()); + System.out.println("#CI:" + ciHeaps.size()); + } + + private void classifyForRemain(Set unknownyet) { + CSDG csdg = new CSDG(); + // build the dependency graph. + for (AllocNode heap : unknownyet) { + Set ifs = notSureFields.getOrDefault(heap, Collections.emptySet()); + boolean cs = false; + boolean existUnknown = false; + Set tos = new HashSet<>(); + for (SparkField sf : ifs) { + PointsToSet pts = pta.reachingObjectsInternal(heap, sf); + for (AllocNode o : pts.toCIPointsToSet().toCollection()) { + // if o is cs, then heap is cs; + if (csHeaps.contains(o)) { + cs = true; + break; + } + if (!ciHeaps.contains(o)) { + // exist unknown. + tos.add(o); + existUnknown = true; + } + } + if (cs) { + break; + } + } + if (cs) { + csHeaps.add(heap); + } else if (existUnknown) { + for (AllocNode to : tos) { + csdg.addEdge(heap, to); + } + } else { + ciHeaps.add(heap); + } + } + // recursively classify heaps for these on CSDG. + System.out.println("#InitOnCSDG:" + csdg.allNodes().size()); + while (true) { + Set noOutDegree = csdg.noOutDegreeNodes(); + if (noOutDegree.isEmpty()) { + break; + } + for (AllocNode nod : noOutDegree) { + if (csHeaps.contains(nod)) { + csHeaps.addAll(csdg.predsOf(nod)); + } else { + ciHeaps.add(nod); + } + csdg.removeNode(nod); + } + } + System.out.println("#StillOnCSDG:" + csdg.allNodes().size()); + ciHeaps.addAll(csdg.allNodes()); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/DFA.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/DFA.java new file mode 100644 index 00000000000..b0f54b14715 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/DFA.java @@ -0,0 +1,102 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.conch; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/* + * This class defines two DFA used for leak analysis and depOnParam analysis introduced in our paper respectively. + * */ +public class DFA { + public enum State { + O, + F, + B, + E, + ERROR; + } + + public enum TranCond { + PARAM, + I_PARAM, + RETURN, + I_RETURN, + THROW, + I_THROW, + ASSIGN, + I_ASSIGN, + LOAD, + I_LOAD, + STORE, + I_STORE, + NEW, + I_NEW, + INTER_STORE, + INTER_ASSIGN, + I_INTER_LOAD; + } + + private static final Map> transitionFunc = new HashMap<>(); + + /* + * This is another automaton for PFG. + * */ + private static final Map> transitionFunc2 = new HashMap<>(); + + static { + // The first DFA introduced in Figure 13 for leak analysis. + Map mO = transitionFunc.computeIfAbsent(State.O, k -> new HashMap<>()); + mO.put(TranCond.NEW, State.F); + + Map mF = transitionFunc.computeIfAbsent(State.F, k -> new HashMap<>()); + mF.put(TranCond.ASSIGN, State.F); + mF.put(TranCond.STORE, State.B); + mF.put(TranCond.INTER_STORE, State.B); + mF.put(TranCond.INTER_ASSIGN, State.F); + mF.put(TranCond.RETURN, State.E); + mF.put(TranCond.THROW, State.E); + + Map mB = transitionFunc.computeIfAbsent(State.B, k -> new HashMap<>()); + mB.put(TranCond.I_ASSIGN, State.B); + mB.put(TranCond.I_LOAD, State.B); + mB.put(TranCond.I_PARAM, State.E); + mB.put(TranCond.I_INTER_LOAD, State.B); + mB.put(TranCond.I_NEW, State.O); + + // The second DFA introduced in Figure 15 for depOnParma analysis. + Map mF2 = transitionFunc2.computeIfAbsent(State.F, k -> new HashMap<>()); + mF2.put(TranCond.NEW, State.F); + mF2.put(TranCond.ASSIGN, State.F); + mF2.put(TranCond.LOAD, State.F); + mF2.put(TranCond.RETURN, State.E); + mF2.put(TranCond.INTER_ASSIGN, State.F); + } + + public static State nextState(State curr, TranCond tranCond) { + return transitionFunc + .getOrDefault(curr, Collections.emptyMap()) + .getOrDefault(tranCond, State.ERROR); + } + + public static State nextState2(TranCond tranCond) { + return transitionFunc2.get(State.F).getOrDefault(tranCond, State.ERROR); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/DepOnParamAnalysis.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/DepOnParamAnalysis.java new file mode 100644 index 00000000000..a391de9be6d --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/DepOnParamAnalysis.java @@ -0,0 +1,162 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.conch; + +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import qilin.core.PTA; +import qilin.core.builder.MethodNodeFactory; +import qilin.core.builder.callgraph.Edge; +import qilin.core.pag.*; +import qilin.util.PTAUtils; +import sootup.core.jimple.common.stmt.JAssignStmt; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.SootMethod; + +/* + * It try to answer where the value of a variable comes from. + * (1) comes from p.f* + * (2) comes from O.f* + * (3) comes from S.f* (S stands for a symbolic heap). + * for the later two case, we are not sure whether the value of current variable comes from the out of its containing method or not. + * We will rely on cs-dependent graph for further checking. + * These two cases is not mentioned in the paper since it does not affect our results. + * */ + +public class DepOnParamAnalysis extends AbstractPAG { + private final Map> pathEdges = new ConcurrentHashMap<>(); + private final Set initialSeeds = ConcurrentHashMap.newKeySet(); + + public DepOnParamAnalysis(PTA prePTA) { + super(prePTA); + build(); + solve(); + } + + protected void solve() { + System.out.println("start analysis!"); + super.solve(); + System.out.println("finish PFG analysis!"); + } + + protected void addParamEdge(LocalVarNode param) { + super.addParamEdge(param); + initialSeeds.add(param); + } + + protected void addNewEdge(AllocNode from, LocalVarNode to) { + super.addNewEdge(from, to); + initialSeeds.add(from); + } + + protected void submitInitialSeeds() { + for (Node node : initialSeeds) { + propagate(node, node); + } + } + + private void propagate(Node srcParam, Node currNode) { + Set fromParams = pathEdges.computeIfAbsent(currNode, k -> ConcurrentHashMap.newKeySet()); + if (!fromParams.contains(srcParam)) { + executor.execute(new PathEdgeProcessingTask(srcParam, currNode)); + } + } + + private class PathEdgeProcessingTask implements Runnable { + private final Node sourceParam; + private final Node currNode; + + public PathEdgeProcessingTask(Node param, Node node) { + this.sourceParam = param; + this.currNode = node; + } + + @Override + public void run() { + pathEdges.computeIfAbsent(currNode, k -> ConcurrentHashMap.newKeySet()).add(sourceParam); + for (TranEdge e : outAndSummaryEdges(currNode)) { + Node nextNode = e.getTarget(); + DFA.TranCond tranCond = e.getTranCond(); + DFA.State nextState = DFA.nextState2(tranCond); + if (nextState == DFA.State.ERROR) { + continue; + } + propagate(sourceParam, nextNode); + if (nextState == DFA.State.E) { + // do something. + SootMethod containingMethod; + if (sourceParam instanceof LocalVarNode) { + LocalVarNode pj = (LocalVarNode) sourceParam; + containingMethod = pj.getMethod(); + } else { + AllocNode heap = (AllocNode) sourceParam; + containingMethod = heap.getMethod(); + } + + Iterator it = + callGraph.edgesInto(new ContextMethod(containingMethod, prePTA.emptyContext())); + while (it.hasNext()) { + Edge edge = it.next(); + SootMethod srcMethod = edge.src(); + MethodPAG srcmpag = prePAG.getMethodPAG(srcMethod); + MethodNodeFactory srcnf = srcmpag.nodeFactory(); + Stmt invokeStmt = edge.srcUnit(); + if (invokeStmt instanceof JAssignStmt) { + JAssignStmt assignStmt = (JAssignStmt) invokeStmt; + + VarNode r = (VarNode) srcnf.getNode(assignStmt.getLeftOp()); + if (sourceParam instanceof LocalVarNode) { + LocalVarNode pj = (LocalVarNode) sourceParam; + VarNode aj = PTAUtils.paramToArg(prePAG, invokeStmt, srcmpag, pj); + if (aj != null) { + addSummaryEdge(new TranEdge(aj, r, DFA.TranCond.INTER_ASSIGN)); + } + } else { + AllocNode symbolHeap = getSymbolicHeapOf(srcMethod, invokeStmt); + addSummaryEdge(new TranEdge(symbolHeap, r, DFA.TranCond.NEW)); + propagate(symbolHeap, symbolHeap); + } + } + } + } + } + } + } + + private void addSummaryEdge(TranEdge tranEdge) { + Node src = tranEdge.getSource(); + Node tgt = tranEdge.getTarget(); + DFA.TranCond tranCond = tranEdge.getTranCond(); + sumEdges.computeIfAbsent(src, k -> ConcurrentHashMap.newKeySet()).add(tranEdge); + for (Node srcParam : pathEdges.getOrDefault(src, Collections.emptySet())) { + DFA.State nextState = DFA.nextState2(tranCond); + if (nextState == DFA.State.ERROR) { + continue; + } + propagate(srcParam, tgt); + } + } + + public Set fetchReachableParamsOf(Node node) { + return pathEdges.getOrDefault(node, Collections.emptySet()); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/LeakAnalysis.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/LeakAnalysis.java new file mode 100644 index 00000000000..b621ac9dcc8 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/LeakAnalysis.java @@ -0,0 +1,220 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.conch; + +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import qilin.core.PTA; +import qilin.core.builder.callgraph.Edge; +import qilin.core.pag.*; +import qilin.util.PTAUtils; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.SootMethod; + +/* + * This structure is used to check whether an object could flow out of its containing method. + * */ +public class LeakAnalysis extends AbstractPAG { + private final Map> pathEdges = new ConcurrentHashMap<>(); + private final Set initialSeeds = ConcurrentHashMap.newKeySet(); + private final Set result = ConcurrentHashMap.newKeySet(); + + public LeakAnalysis(PTA prePTA) { + super(prePTA); + build(); + solve(); + } + + protected void solve() { + System.out.println("start analysis!"); + super.solve(); + System.out.println("finish MFG analysis!"); + } + + protected void submitInitialSeeds() { + initialSeeds.forEach(pe -> executor.execute(new PathEdgeProcessingTask(pe))); + } + + protected void addThrowEdge(Node throwNode) { + super.addThrowEdge(throwNode); + initialSeeds.add(new PathEdge(throwNode, DFA.State.B, throwNode, DFA.State.B)); + } + + protected void addParamEdge(LocalVarNode param) { + super.addParamEdge(param); + initialSeeds.add(new PathEdge(param, DFA.State.F, param, DFA.State.F)); + } + + protected void addReturnEdge(LocalVarNode mret) { + super.addReturnEdge(mret); + initialSeeds.add(new PathEdge(mret, DFA.State.B, mret, DFA.State.B)); + } + + protected void addNewEdge(AllocNode from, LocalVarNode to) { + super.addNewEdge(from, to); + initialSeeds.add(new PathEdge(from, DFA.State.O, from, DFA.State.O)); + } + + private void addPathEdge(PathEdge pe) { + Node tgtNode = pe.getTgtNode(); + pathEdges.computeIfAbsent(tgtNode, k -> ConcurrentHashMap.newKeySet()).add(pe); + } + + private boolean containPathEdge(PathEdge pe) { + Node tgtNode = pe.getTgtNode(); + return pathEdges.getOrDefault(tgtNode, Collections.emptySet()).contains(pe); + } + + private void propagate(PathEdge pe) { + if (!containPathEdge(pe)) { + executor.execute(new PathEdgeProcessingTask(pe)); + } + } + + private class PathEdgeProcessingTask implements Runnable { + PathEdge pe; + + public PathEdgeProcessingTask(PathEdge pe) { + this.pe = pe; + } + + @Override + public void run() { + addPathEdge(pe); + DFA.State initState = pe.getSrcState(); + Node sourceNode = pe.getSrcNode(); + DFA.State targetState = pe.getTgtState(); + Node targetNode = pe.getTgtNode(); + + for (TranEdge e : outAndSummaryEdges(targetNode)) { + Node newTargetNode = e.getTarget(); + DFA.TranCond tranCond = e.getTranCond(); + DFA.State nextState = DFA.nextState(targetState, tranCond); + if (nextState == DFA.State.ERROR) { + continue; + } + if (initState == DFA.State.B && nextState == DFA.State.O) { + // disallow such kinds of pathedge: --> + continue; + } + PathEdge nPE = new PathEdge(sourceNode, initState, newTargetNode, nextState); + propagate(nPE); + if (nextState != DFA.State.E) { + continue; + } + // reach the end state. + if (initState == DFA.State.O) { + // report a heap flows out of its containing method. + AllocNode sourceHeap = (AllocNode) sourceNode; + result.add(sourceHeap); + SootMethod containingMethod = sourceHeap.getMethod(); + Iterator it = + callGraph.edgesInto(new ContextMethod(containingMethod, prePTA.emptyContext())); + while (it.hasNext()) { + Edge edge = it.next(); + SootMethod srcMethod = edge.src(); + MethodPAG srcmpag = prePAG.getMethodPAG(srcMethod); + Stmt invokeStmt = edge.srcUnit(); + if (targetState == DFA.State.F) { // ret.f* = heap + // add S -new-> r summary edge for symbolic heaps. + VarNode ret = (VarNode) targetNode; + VarNode r = PTAUtils.paramToArg(prePAG, invokeStmt, srcmpag, ret); + if (r != null) { + AllocNode s = getSymbolicHeapOf(srcMethod, invokeStmt); + addSummaryEdge(new TranEdge(s, r, DFA.TranCond.NEW)); + addSummaryEdge(new TranEdge(r, s, DFA.TranCond.I_NEW)); + } + } + } + } else if (initState == DFA.State.F) { + LocalVarNode pj = (LocalVarNode) sourceNode; + SootMethod containingMethod = pj.getMethod(); + Iterator it = + callGraph.edgesInto(new ContextMethod(containingMethod, prePTA.emptyContext())); + while (it.hasNext()) { + Edge edge = it.next(); + SootMethod srcMethod = edge.src(); + MethodPAG srcmpag = prePAG.getMethodPAG(srcMethod); + Stmt invokeStmt = edge.srcUnit(); + VarNode aj = PTAUtils.paramToArg(prePAG, invokeStmt, srcmpag, pj); + // a param reach end state. + if (targetState == DFA.State.B && sourceNode != targetNode) { // pi.f* = pj, pi != pj. + // add aj --> ai summary edge. inter_store. + VarNode pi = (VarNode) targetNode; + VarNode ai = PTAUtils.paramToArg(prePAG, invokeStmt, srcmpag, pi); + if (ai != null && aj != null && ai != aj) { + addSummaryEdge(new TranEdge(aj, ai, DFA.TranCond.INTER_STORE)); + } + } else if (targetState == DFA.State.F) { // ret.f* = pj + // add aj --> r summary edge. inter_load. + VarNode ret = (VarNode) targetNode; + VarNode r = PTAUtils.paramToArg(prePAG, invokeStmt, srcmpag, ret); + if (r != null && aj != null) { + addSummaryEdge(new TranEdge(aj, r, DFA.TranCond.INTER_ASSIGN)); + } + } + } + } else if (initState == DFA.State.B && targetState == DFA.State.B) { + // ret = pi.f* + LocalVarNode retOrThrow = (LocalVarNode) sourceNode; + SootMethod containingMethod = retOrThrow.getMethod(); + Iterator it = + callGraph.edgesInto(new ContextMethod(containingMethod, prePTA.emptyContext())); + VarNode pi = (VarNode) pe.getTgtNode(); + // add r --> ai summary edge inverse_inter_load. + while (it.hasNext()) { + Edge edge = it.next(); + SootMethod srcMethod = edge.src(); + MethodPAG srcmpag = prePAG.getMethodPAG(srcMethod); + Stmt invokeStmt = edge.srcUnit(); + VarNode ai = PTAUtils.paramToArg(prePAG, invokeStmt, srcmpag, pi); + VarNode r = PTAUtils.paramToArg(prePAG, invokeStmt, srcmpag, retOrThrow); + if (r != null && ai != null) { + addSummaryEdge(new TranEdge(r, ai, DFA.TranCond.I_INTER_LOAD)); + } + } + } + } + } + } + + private void addSummaryEdge(TranEdge tranEdge) { + Node src = tranEdge.getSource(); + Node tgt = tranEdge.getTarget(); + DFA.TranCond tranCond = tranEdge.getTranCond(); + sumEdges.computeIfAbsent(src, k -> ConcurrentHashMap.newKeySet()).add(tranEdge); + for (PathEdge pe : pathEdges.getOrDefault(src, Collections.emptySet())) { + DFA.State tgtState = pe.getTgtState(); + DFA.State nextState = DFA.nextState(tgtState, tranCond); + if (nextState == DFA.State.ERROR) { + continue; + } + PathEdge nPE = new PathEdge(pe.getSrcNode(), pe.getSrcState(), tgt, nextState); + propagate(nPE); + } + } + + // Condition(A): heap that could flow out of its containing methods. + public boolean isLeakObject(AllocNode heap) { + return result.contains(heap); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/PathEdge.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/PathEdge.java new file mode 100644 index 00000000000..88fa1fb3b94 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/PathEdge.java @@ -0,0 +1,93 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.conch; + +import qilin.core.pag.Node; + +/* + * This is the path edge defined as the standard in IFDS algorithm. + * */ +public class PathEdge { + final Node srcNode; + final DFA.State srcState; + final Node tgtNode; + final DFA.State tgtState; + final int hashCode; + + public PathEdge(Node srcNode, DFA.State srcState, Node tgtNode, DFA.State tgtState) { + this.srcNode = srcNode; + this.srcState = srcState; + this.tgtNode = tgtNode; + this.tgtState = tgtState; + + final int prime = 31; + int result = 1; + result = prime * result + ((srcNode == null) ? 0 : srcNode.hashCode()); + result = prime * result + ((srcState == null) ? 0 : srcState.hashCode()); + result = prime * result + ((tgtNode == null) ? 0 : tgtNode.hashCode()); + result = prime * result + ((tgtState == null) ? 0 : tgtState.hashCode()); + this.hashCode = result; + } + + public Node getSrcNode() { + return srcNode; + } + + public DFA.State getSrcState() { + return srcState; + } + + public Node getTgtNode() { + return tgtNode; + } + + public DFA.State getTgtState() { + return tgtState; + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + PathEdge other = (PathEdge) obj; + if (srcNode == null) { + if (other.srcNode != null) return false; + } else if (!srcNode.equals(other.srcNode)) return false; + if (tgtNode == null) { + if (other.tgtNode != null) return false; + } else if (!tgtNode.equals(other.tgtNode)) return false; + if (srcState == null) { + if (other.srcState != null) return false; + } else if (!srcState.equals(other.srcState)) return false; + if (tgtState == null) { + return other.tgtState == null; + } else return tgtState.equals(other.tgtState); + } + + @Override + public String toString() { + return "(" + srcNode + "," + srcState + ")\n\t" + "-->" + "(" + tgtNode + "," + tgtState + ")"; + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/SMPAG.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/SMPAG.java new file mode 100644 index 00000000000..361c38a4db3 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/SMPAG.java @@ -0,0 +1,103 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.conch; + +import java.util.HashSet; +import java.util.Set; +import qilin.core.pag.*; +import qilin.util.Pair; +import qilin.util.graph.DirectedGraphImpl; +import qilin.util.queue.QueueReader; +import qilin.util.queue.UniqueQueue; + +/* + * a simplified method PAG with the effects of static load and store being eliminated. + * */ +public class SMPAG extends DirectedGraphImpl { + MethodPAG srcmpag; + Set> loads; + Set> stores; + + public SMPAG(MethodPAG srcmpag) { + this.srcmpag = srcmpag; + init(); + } + + private void init() { + loads = new HashSet<>(); + stores = new HashSet<>(); + QueueReader reader = srcmpag.getInternalReader().clone(); + UniqueQueue workList = new UniqueQueue<>(); + Set visit = new HashSet<>(); + while (reader.hasNext()) { + qilin.core.pag.Node from = reader.next(), to = reader.next(); + if (from instanceof LocalVarNode) { + if (to instanceof LocalVarNode) { + this.addEdge(from, to); // ASSIGN + } else if (to instanceof FieldRefNode) { + this.addEdge(from, to); // STORE + } // local-global : A.f = a; + + } else if (from instanceof AllocNode) { + this.addEdge(from, to); // NEW + } else if (from instanceof FieldRefNode) { + this.addEdge(from, to); // LOAD + } else { // global-local + workList.add(to); + } + } + while (!workList.isEmpty()) { + Node curr = workList.poll(); + visit.add(curr); + if (this.predsOf(curr).isEmpty()) { + for (Node next : this.succsOf(curr)) { + if (!visit.contains(next)) { + workList.add(next); + } + this.preds.get(next).remove(curr); + } + if (this.succs.containsKey(curr)) { + this.succs.get(curr).clear(); + } + } + } + + // initialize stores and loads + for (Node node : this.allNodes()) { + if (node instanceof FieldRefNode) { + for (Node from : this.predsOf(node)) { + stores.add(new Pair<>(node, from)); + } + for (Node to : this.succsOf(node)) { + loads.add(new Pair<>(to, node)); + } + } + } + } + + // + public Set> getStores() { + return stores; + } + + // + public Set> getLoads() { + return loads; + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/TranEdge.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/TranEdge.java new file mode 100644 index 00000000000..149c90686e8 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/TranEdge.java @@ -0,0 +1,69 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.conch; + +import java.util.Objects; +import qilin.core.pag.Node; + +/* + * Transition edges used in define our DFAs. + * */ +public class TranEdge { + private final Node src; + private final Node dst; + private final DFA.TranCond tranCond; + + public TranEdge(Node s, Node d, DFA.TranCond tran) { + this.src = s; + this.dst = d; + this.tranCond = tran; + } + + public Node getSource() { + return src; + } + + public Node getTarget() { + return dst; + } + + public DFA.TranCond getTranCond() { + return tranCond; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TranEdge tranEdge = (TranEdge) o; + return Objects.equals(src, tranEdge.src) + && Objects.equals(dst, tranEdge.dst) + && tranCond == tranEdge.tranCond; + } + + @Override + public int hashCode() { + return Objects.hash(src, dst, tranCond); + } + + @Override + public String toString() { + return src + "\n\t--" + tranCond + "-->\n\t" + dst; + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/Trilean.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/Trilean.java new file mode 100644 index 00000000000..3be728e074b --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/Trilean.java @@ -0,0 +1,35 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.conch; + +public enum Trilean { + TRUE, + FALSE, + UNKNOWN; + + public static Trilean OR(Trilean x1, Trilean x2) { + if (x1 == TRUE || x2 == TRUE) { + return TRUE; + } else if (x1 == UNKNOWN || x2 == UNKNOWN) { + return UNKNOWN; + } else { + return FALSE; + } + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/dd/CtxTunnelingFeaturesTrueTable.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/dd/CtxTunnelingFeaturesTrueTable.java new file mode 100644 index 00000000000..61f15ebeb30 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/dd/CtxTunnelingFeaturesTrueTable.java @@ -0,0 +1,271 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.dd; + +import qilin.util.PTAUtils; +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.basic.Value; +import sootup.core.jimple.common.expr.AbstractInvokeExpr; +import sootup.core.jimple.common.expr.JInterfaceInvokeExpr; +import sootup.core.jimple.common.expr.JNewArrayExpr; +import sootup.core.jimple.common.expr.JNewExpr; +import sootup.core.jimple.common.expr.JNewMultiArrayExpr; +import sootup.core.jimple.common.expr.JStaticInvokeExpr; +import sootup.core.jimple.common.expr.JVirtualInvokeExpr; +import sootup.core.jimple.common.ref.JArrayRef; +import sootup.core.jimple.common.ref.JInstanceFieldRef; +import sootup.core.jimple.common.stmt.JAssignStmt; +import sootup.core.jimple.common.stmt.JInvokeStmt; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.Body; +import sootup.core.model.SootClass; +import sootup.core.model.SootMethod; +import sootup.core.views.View; + +/* + * features and formulas used in "Precise and Scalable Points-to Analysis via Data-Driven + * Context Tunneling" (OOPSLA 2018). + * */ +public class CtxTunnelingFeaturesTrueTable { + private final View view; + private final boolean[] f = new boolean[24]; + + public CtxTunnelingFeaturesTrueTable(View view, SootMethod sm) { + this.view = view; + String sig = sm.getSignature().toString(); + // the 10 atomic signature features. + this.f[1] = sig.contains("java"); + this.f[2] = sig.contains("lang"); + this.f[3] = sig.contains("sun"); + this.f[4] = sig.contains("()"); + this.f[5] = sig.contains("void"); + this.f[6] = sig.contains("security"); + this.f[7] = sig.contains("int"); + this.f[8] = sig.contains("util"); + this.f[9] = sig.contains("String"); + this.f[10] = sig.contains("init"); + // 13 additional features (a bit complex). + this.f[11] = containedInNestedClass(sm); + this.f[12] = sm.getParameterCount() > 1; + + Body body = PTAUtils.getMethodBody(sm); + this.f[15] = body.getLocalCount() > 0; + int heapAllocCnt = 0; + for (Stmt unit : body.getStmts()) { + if (unit instanceof JAssignStmt) { + JAssignStmt assignStmt = (JAssignStmt) unit; + Value left = assignStmt.getLeftOp(); + if (left instanceof Local) { + Value right = assignStmt.getRightOp(); + if (right instanceof Local) { + this.f[14] = true; + } else if (right instanceof JNewExpr + || right instanceof JNewArrayExpr + || right instanceof JNewMultiArrayExpr) { + heapAllocCnt++; + } else if (right instanceof AbstractInvokeExpr) { + if (right instanceof JStaticInvokeExpr) { + this.f[17] = true; + } else if (right instanceof JVirtualInvokeExpr + || right instanceof JInterfaceInvokeExpr) { + this.f[18] = true; + } + } else if (right instanceof JArrayRef) { + this.f[13] = true; + } + } else if (left instanceof JInstanceFieldRef) { + this.f[16] = true; + } + } else if (unit instanceof JInvokeStmt) { + JInvokeStmt invokeStmt = (JInvokeStmt) unit; + AbstractInvokeExpr expr = invokeStmt.getInvokeExpr(); + if (expr instanceof JStaticInvokeExpr) { + this.f[17] = true; + } else if (expr instanceof JVirtualInvokeExpr || expr instanceof JInterfaceInvokeExpr) { + this.f[18] = true; + } + } + } + this.f[19] = sm.isStatic(); + this.f[20] = heapAllocCnt == 1; + this.f[21] = sig.contains("Object"); + this.f[22] = + heapAllocCnt + >= 1; // note, the original implementation is >=1 not > 1 which is conflict with the + // paper. + SootClass sc = view.getClass(sm.getDeclaringClassType()).get(); + this.f[23] = sc.getMethods().size() > 20; // their artifact uses 20 as the threshold. + } + + public boolean containedInNestedClass(SootMethod sm) { + SootClass sc = view.getClass(sm.getDeclaringClassType()).get(); + return sc.toString().contains("$"); + } + + // corresponding to the Tunneling predicate in its original implementation. + public boolean cfaFormula2() { + boolean subF1 = !f[3] && !f[6] && !f[9] && f[14] && f[15] && !f[18] && !f[19] && !f[23]; + boolean subF2 = + f[1] && !f[3] && !f[4] && f[7] && !f[9] && f[12] && f[14] && f[15] && !f[16] && !f[19] + && !f[21]; + boolean subF3 = + f[1] && !f[2] && !f[3] && !f[6] && !f[9] && f[11] && !f[13] && f[14] && f[15] && !f[16] + && !f[17] && !f[19] && !f[20] && !f[21] && !f[22] && !f[23]; + return subF1 || subF2 || subF3; + } + + // corresponding to the TunnelingM in its original implementation. + public boolean cfaFormula1() { + boolean subF1 = + f[1] && !f[2] && !f[3] && f[4] && f[5] && !f[6] && !f[7] && f[8] && f[10] && !f[11] + && !f[12] && !f[13] && f[14] && f[15] && !f[16] && !f[17] && !f[18] && !f[19] && !f[20] + && !f[21] && !f[22] && f[23]; + + boolean subF2 = + !f[6] && f[8] && !f[10] && !f[11] && f[14] && f[15] && !f[16] && !f[17] && !f[18] && !f[19] + && !f[20] && !f[22]; + + boolean subF3 = + f[1] && f[2] && !f[3] && !f[4] && !f[6] && f[8] && !f[9] && !f[10] && !f[11] && f[12] + && f[14] && f[15] && !f[16] && f[17] && f[18] && f[19] && !f[20] && f[21] && !f[22] + && f[23]; + return subF1 || subF2 || subF3; + } + + public boolean objFormula2() { + boolean subF1 = f[1] && !f[3] && !f[6] && !f[9] && f[11] && f[14] && f[15] && !f[19]; + boolean subF2 = + f[1] && f[2] && f[3] && !f[4] && f[5] && f[6] && !f[7] && !f[8] && f[9] && f[10] && !f[11] + && f[12] && !f[13] && f[14] && f[15] && f[16] && f[17] && f[18] && !f[19] && !f[20] + && !f[21] && f[22] && f[23]; + return subF1 || subF2; + } + + public boolean objFormula1() { + boolean subF1 = + !f[2] && !f[3] && !f[4] && f[5] && !f[6] && !f[7] && !f[8] && !f[9] && f[10] && !f[11] + && !f[12] && !f[13] && f[14] && f[15] && f[16] && !f[17] && !f[18] && !f[19] && f[20] + && !f[21] && f[22] && !f[23]; + + boolean subF2 = + f[1] && !f[2] && !f[3] && f[4] && !f[5] && !f[6] && !f[7] && !f[9] && !f[10] && !f[11] + && !f[12] && !f[13] && f[14] && f[15] && !f[16] && !f[17] && !f[18] && !f[19] && f[20] + && !f[21] && !f[23]; + + boolean subF3 = + f[1] && !f[2] && !f[3] && !f[4] && f[5] && !f[6] && f[7] && f[8] && !f[9] && f[10] && !f[11] + && f[12] && !f[13] && f[14] && f[15] && f[16] && !f[17] && !f[18] && !f[19] && f[20] + && !f[21] && f[22] && !f[23]; + + boolean subF4 = + f[1] && f[2] && !f[3] && !f[4] && !f[5] && !f[6] && !f[7] && f[8] && !f[10] && !f[11] + && !f[12] && !f[13] && f[14] && f[15] && !f[16] && !f[17] && !f[18] && f[21] && f[23]; + return subF1 || subF2 || subF3 || subF4; + } + + public boolean typeFormula2() { + boolean subF1 = f[1] && !f[3] && !f[6] && !f[9] && f[11] && f[14] && f[15] && !f[19] && !f[23]; + boolean subF2 = + f[1] && f[2] && !f[5] && !f[7] && !f[8] && f[9] && !f[10] && !f[11] && f[14] && f[15] + && !f[16] && f[17] && !f[19] && !f[20] && f[22] && !f[23]; + boolean subF3 = + f[3] && f[5] && !f[6] && !f[7] && !f[8] && !f[9] && !f[11] && !f[13] && f[14] && f[15] + && f[18] && !f[19] && !f[20] && !f[22] && f[23]; + + boolean subF4 = + f[1] && !f[2] && !f[3] && !f[4] && f[5] && !f[6] && !f[7] && !f[8] && !f[9] && f[10] + && !f[11] && f[14] && f[15] && f[16] && !f[17] && !f[19] && !f[20] && !f[21]; + + boolean subF5 = + !f[1] && !f[2] && !f[3] && !f[4] && !f[6] && f[7] && !f[9] && !f[11] && f[12] && !f[13] + && f[14] && f[15] && !f[17] && !f[19] && !f[20] && !f[21] && !f[23]; + return subF1 || subF2 || subF3 || subF4 || subF5; + } + + public boolean typeFormula1() { + boolean subF1 = + f[1] && !f[2] && !f[3] && !f[6] && !f[7] && f[8] && !f[9] && !f[10] && !f[11] && !f[12] + && f[14] && f[15] && !f[16] && !f[17] && !f[19] && f[20] && f[22]; + + boolean subF2 = + f[1] && !f[2] && !f[3] && f[4] && f[5] && !f[6] && !f[7] && f[8] && !f[9] && f[10] && !f[11] + && !f[12] && !f[13] && !f[14] && f[15] && !f[16] && !f[17] && !f[18] && !f[19] && !f[20] + && !f[21] && f[22] && f[23]; + + boolean subF3 = + f[1] && f[2] && !f[3] && !f[4] && f[5] && !f[6] && !f[7] && !f[8] && !f[9] && f[10] + && !f[11] && f[12] && !f[13] && f[14] && f[15] && f[16] && !f[17] && !f[18] && !f[19] + && !f[20] && !f[21] && f[22] && f[23]; + + boolean subF4 = + f[1] && !f[2] && !f[3] && !f[4] && f[5] && !f[6] && f[7] && f[8] && !f[9] && f[10] && !f[11] + && !f[12] && !f[13] && f[14] && f[15] && f[16] && !f[17] && !f[21] && !f[18] && f[20] + && f[22] && !f[23]; + + boolean subF5 = + !f[1] && !f[2] && !f[3] && !f[4] && !f[5] && !f[6] && f[7] && !f[8] && !f[9] && !f[10] + && !f[11] && f[12] && !f[13] && f[14] && f[15] && !f[16] && !f[17] && f[18] && !f[19] + && !f[20] && !f[21] && f[22] && f[23]; + + boolean subF6 = + f[1] && !f[2] && !f[3] && !f[4] && f[5] && !f[6] && f[7] && !f[9] && f[8] && f[10] && !f[11] + && f[12] && !f[13] && f[14] && f[15] && f[16] && !f[17] && !f[18] && !f[19] && f[20] + && !f[21] && f[22] && !f[23]; + + boolean subF7 = + !f[1] && !f[2] && f[3] && f[4] && f[5] && !f[6] && !f[7] && !f[8] && !f[9] && f[10] + && !f[11] && !f[12] && !f[13] && !f[14] && f[15] && !f[16] && !f[17] && !f[18] && !f[19] + && !f[20] && !f[21] && f[22] && !f[23]; + + boolean subF8 = + !f[4] && !f[9] && !f[10] && !f[11] && f[13] && f[14] && f[15] && !f[16] && f[18] && f[22]; + + boolean subF9 = + f[1] && !f[2] && !f[3] && f[4] && !f[5] && !f[6] && !f[7] && f[8] && !f[9] && !f[10] + && !f[11] && !f[12] && !f[13] && f[14] && f[15] && !f[16] && !f[17] && f[18] && !f[19] + && !f[20] && !f[21] && f[22] && f[23]; + + boolean subF10 = + !f[1] && !f[2] && !f[3] && !f[4] && f[5] && !f[6] && !f[7] && !f[9] && !f[11] && !f[12] + && !f[13] && f[14] && f[15] && !f[16] && !f[17] && !f[19] && !f[21] && f[22]; + return subF1 || subF2 || subF3 || subF4 || subF5 || subF6 || subF7 || subF8 || subF9 || subF10; + } + + public boolean hybridFormula2() { + boolean subF1 = f[1] && !f[3] && !f[6] && !f[9] && f[11] && f[15] && !f[23]; + boolean subF2 = + f[1] && !f[3] && !f[4] && !f[6] && f[8] && !f[9] && !f[10] && !f[11] && f[14] && f[15] + && !f[16] && f[19]; + boolean subF3 = + f[2] && !f[3] && f[4] && !f[5] && !f[6] && f[8] && !f[9] && !f[11] && !f[13] && f[14] + && f[15] && !f[16] && f[17] && !f[18] && !f[19] && !f[20] && f[23]; + return subF1 || subF2 || subF3; + } + + public boolean hybridFormula1() { + boolean subF1 = + f[1] && !f[2] && !f[3] && f[4] && !f[6] && !f[8] && !f[9] && !f[11] && !f[13] && f[14] + && f[15] && !f[17] && !f[19] && f[20] && !f[21] && f[22]; + boolean subF2 = + f[1] && !f[2] && !f[3] && !f[4] && f[5] && !f[6] && f[7] && f[8] && !f[9] && f[10] && !f[11] + && !f[13] && !f[14] && f[15] && f[16] && !f[17] && !f[18] && !f[19] && f[20] && !f[21] + && f[22] && !f[23]; + return subF1 || subF2; + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/dd/DataDrivenSelector.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/dd/DataDrivenSelector.java new file mode 100644 index 00000000000..8b1bd71d644 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/dd/DataDrivenSelector.java @@ -0,0 +1,94 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.dd; + +import java.util.HashMap; +import java.util.Map; +import qilin.core.context.Context; +import qilin.core.pag.AllocNode; +import qilin.core.pag.FieldValNode; +import qilin.core.pag.LocalVarNode; +import qilin.parm.ctxcons.CallsiteCtxConstructor; +import qilin.parm.ctxcons.HybObjCtxConstructor; +import qilin.parm.ctxcons.ObjCtxConstructor; +import qilin.parm.ctxcons.TypeCtxConstructor; +import qilin.parm.select.CtxSelector; +import sootup.core.model.SootMethod; + +public class DataDrivenSelector extends CtxSelector { + private final Map m2ftt = new HashMap<>(); + private final Class mClass; + + public DataDrivenSelector(Class mClass) { + this.mClass = mClass; + } + + private FeaturesTrueTable findOrCreateFeaturesTrueTable(SootMethod sm) { + return m2ftt.computeIfAbsent(sm, k -> new FeaturesTrueTable(sm)); + } + + @Override + public Context select(SootMethod m, Context context) { + FeaturesTrueTable ftt = findOrCreateFeaturesTrueTable(m); + int i = 0; + if (mClass == HybObjCtxConstructor.class) { + if (ftt.hybrid2objFormula2()) { + i = 2; + } else if (ftt.hybrid2objFormula1()) { + i = 1; + } + } else if (mClass == ObjCtxConstructor.class) { + if (ftt.twoObjFormula2()) { + i = 2; + } else if (ftt.twoObjFormula1()) { + i = 1; + } + } else if (mClass == CallsiteCtxConstructor.class) { + if (ftt.twoCFAFormula2()) { + i = 2; + } else if (ftt.twoCFAFormula1()) { + i = 1; + } + } else if (mClass == TypeCtxConstructor.class) { + if (ftt.twoTypeFormula2()) { + i = 2; + } else if (ftt.twoTypeFormula1()) { + i = 1; + } + } else { + throw new RuntimeException("unsupport data-driven pointer analysis!"); + } + return contextTailor(context, i); + } + + @Override + public Context select(LocalVarNode lvn, Context context) { + return context; + } + + @Override + public Context select(FieldValNode fvn, Context context) { + return context; + } + + @Override + public Context select(AllocNode heap, Context context) { + return context; + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/dd/FeaturesTrueTable.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/dd/FeaturesTrueTable.java new file mode 100644 index 00000000000..0cab2b67693 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/dd/FeaturesTrueTable.java @@ -0,0 +1,319 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.dd; + +import qilin.util.PTAUtils; +import sootup.core.jimple.common.stmt.JAssignStmt; +import sootup.core.jimple.common.stmt.JGotoStmt; +import sootup.core.jimple.common.stmt.JIdentityStmt; +import sootup.core.jimple.common.stmt.JIfStmt; +import sootup.core.jimple.common.stmt.JInvokeStmt; +import sootup.core.jimple.common.stmt.JNopStmt; +import sootup.core.jimple.common.stmt.JReturnStmt; +import sootup.core.jimple.common.stmt.JReturnVoidStmt; +import sootup.core.jimple.common.stmt.JThrowStmt; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.jimple.javabytecode.stmt.JBreakpointStmt; +import sootup.core.jimple.javabytecode.stmt.JEnterMonitorStmt; +import sootup.core.jimple.javabytecode.stmt.JExitMonitorStmt; +import sootup.core.jimple.javabytecode.stmt.JRetStmt; +import sootup.core.jimple.javabytecode.stmt.JSwitchStmt; +import sootup.core.model.Body; +import sootup.core.model.SootMethod; + +/* + * features and formulas used in "Data-driven context-sensitivity for Points-to Analysis (OOPSLA 2017)" + * */ +public class FeaturesTrueTable { + private final boolean[] features = new boolean[26]; + + public FeaturesTrueTable(SootMethod sm) { + String sig = sm.getSignature().toString(); + this.features[1] = sig.contains("java"); + this.features[2] = sig.contains("lang"); + this.features[3] = sig.contains("sun"); + this.features[4] = sig.contains("()"); + this.features[5] = sig.contains("void"); + this.features[6] = sig.contains("security"); + this.features[7] = sig.contains("int"); + this.features[8] = sig.contains("util"); + this.features[9] = sig.contains("String"); + this.features[10] = sig.contains("init"); + Body body = PTAUtils.getMethodBody(sm); + for (Stmt unit : body.getStmts()) { + if (unit instanceof JAssignStmt) { + this.features[11] = true; + } else if (unit instanceof JIdentityStmt) { + this.features[12] = true; + } else if (unit instanceof JInvokeStmt) { + this.features[13] = true; + } else if (unit instanceof JReturnStmt) { + this.features[14] = true; + } else if (unit instanceof JThrowStmt) { + this.features[15] = true; + } else if (unit instanceof JBreakpointStmt) { + this.features[16] = true; + } else if (unit instanceof JEnterMonitorStmt) { + this.features[17] = true; + } else if (unit instanceof JExitMonitorStmt) { + this.features[18] = true; + } else if (unit instanceof JGotoStmt) { + this.features[19] = true; + } else if (unit instanceof JIfStmt) { + this.features[20] = true; + } else if (unit instanceof JSwitchStmt) { + this.features[21] = true; + } else if (unit instanceof JNopStmt) { + this.features[22] = true; + } else if (unit instanceof JRetStmt) { + this.features[23] = true; + } else if (unit instanceof JReturnVoidStmt) { + this.features[24] = true; + } else if (unit instanceof JSwitchStmt) { + this.features[25] = true; + } + } + } + + // the following two are for hybrid 2-object-sensitive pointer analysis. + public boolean hybrid2objFormula2() { + return features[1] + && !features[3] + && !features[6] + && features[8] + && !features[9] + && !features[16] + && !features[17] + && !features[18] + && !features[19] + && !features[20] + && !features[21] + && !features[22] + && !features[23] + && !features[24] + && !features[25]; + } + + public boolean hybrid2objFormula1() { + boolean subF1 = + features[1] + && !features[3] + && !features[4] + && features[6] + && !features[7] + && !features[8] + && !features[9] + && !features[15] + && !features[16] + && !features[17] + && !features[18] + && !features[19] + && !features[20] + && !features[21] + && !features[22] + && !features[23] + && !features[24] + && !features[25]; + boolean subF2 = + !features[3] + && !features[4] + && !features[7] + && !features[8] + && !features[9] + && features[10] + && features[11] + && features[12] + && features[13] + && !features[16] + && !features[17] + && !features[18] + && !features[19] + && !features[20] + && !features[21] + && !features[22] + && !features[23] + && !features[24] + && !features[25]; + boolean subF3 = + !features[3] + && !features[9] + && features[13] + && features[14] + && features[15] + && !features[16] + && !features[17] + && !features[18] + && !features[19] + && !features[20] + && !features[21] + && !features[22] + && !features[23] + && !features[24] + && !features[25]; + boolean subF4 = + features[1] + && features[2] + && !features[3] + && features[4] + && !features[5] + && !features[6] + && !features[7] + && !features[8] + && !features[9] + && !features[10] + && !features[13] + && !features[15] + && !features[16] + && !features[17] + && !features[18] + && !features[19] + && !features[20] + && !features[21] + && !features[22] + && !features[23] + && !features[24] + && !features[25]; + return subF1 || subF2 || subF3 || subF4; + } + + public boolean twoObjFormula2() { + return hybrid2objFormula2(); + } + + public boolean twoObjFormula1() { + boolean subF1 = + features[1] + && features[2] + && !features[3] + && !features[6] + && !features[7] + && !features[8] + && !features[9] + && !features[16] + && !features[17] + && !features[18] + && !features[19] + && !features[20] + && !features[21] + && !features[22] + && !features[23] + && !features[24] + && !features[25]; + boolean subF2 = + !features[1] + && !features[2] + && features[5] + && features[8] + && !features[9] + && features[11] + && features[12] + && !features[14] + && !features[15] + && !features[16] + && !features[17] + && !features[18] + && !features[19] + && !features[20] + && !features[21] + && !features[22] + && !features[23] + && !features[24] + && !features[25]; + boolean subF3 = + !features[3] + && !features[4] + && !features[7] + && !features[8] + && !features[9] + && features[10] + && features[11] + && features[12] + && !features[16] + && !features[17] + && !features[18] + && !features[19] + && !features[20] + && !features[21] + && !features[22] + && !features[23] + && !features[24] + && !features[25]; + return subF1 || subF2 || subF3; + } + + public boolean twoTypeFormula2() { + return hybrid2objFormula2(); + } + + public boolean twoTypeFormula1() { + return features[1] + && features[2] + && !features[3] + && !features[6] + && !features[7] + && !features[8] + && !features[9] + && !features[15] + && !features[16] + && !features[17] + && !features[18] + && !features[19] + && !features[20] + && !features[21] + && !features[22] + && !features[23] + && !features[24] + && !features[25]; + } + + public boolean twoCFAFormula2() { + return features[1] + && !features[6] + && !features[7] + && features[11] + && features[12] + && features[13] + && !features[16] + && !features[17] + && !features[18] + && !features[19] + && !features[20] + && !features[21] + && !features[22] + && !features[23] + && !features[24] + && !features[25]; + } + + public boolean twoCFAFormula1() { + return features[1] + && features[2] + && !features[7] + && !features[16] + && !features[17] + && !features[18] + && !features[19] + && !features[20] + && !features[21] + && !features[22] + && !features[23] + && !features[24] + && !features[25]; + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/dd/TunnelingConstructor.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/dd/TunnelingConstructor.java new file mode 100644 index 00000000000..03f8751e0b1 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/dd/TunnelingConstructor.java @@ -0,0 +1,72 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.dd; + +import java.util.HashMap; +import java.util.Map; +import qilin.core.context.Context; +import qilin.core.pag.CallSite; +import qilin.core.pag.ContextAllocNode; +import qilin.core.pag.ContextMethod; +import qilin.parm.ctxcons.*; +import sootup.core.model.SootMethod; +import sootup.core.views.View; + +public class TunnelingConstructor implements CtxConstructor { + private final View view; + private final CtxConstructor ctxCons; + private final Map m2ftt = new HashMap<>(); + + private CtxTunnelingFeaturesTrueTable findOrCreateTunnelingFeaturesTrueTable(SootMethod sm) { + return m2ftt.computeIfAbsent(sm, k -> new CtxTunnelingFeaturesTrueTable(view, sm)); + } + + public TunnelingConstructor(View view, CtxConstructor ctxCons) { + this.view = view; + this.ctxCons = ctxCons; + } + + @Override + public Context constructCtx( + ContextMethod caller, ContextAllocNode receiverNode, CallSite callSite, SootMethod target) { + CtxTunnelingFeaturesTrueTable ctftt1 = findOrCreateTunnelingFeaturesTrueTable(caller.method()); + CtxTunnelingFeaturesTrueTable ctftt2 = findOrCreateTunnelingFeaturesTrueTable(target); + if (ctxCons instanceof CallsiteCtxConstructor) { + if (ctftt1.cfaFormula1() || ctftt2.cfaFormula2()) { + return caller.context(); + } + } else if (ctxCons instanceof TypeCtxConstructor) { + if (ctftt1.typeFormula1() || ctftt2.typeFormula2()) { + return caller.context(); + } + } else if (ctxCons instanceof ObjCtxConstructor) { + if (ctftt1.objFormula1() || ctftt2.objFormula2()) { + return caller.context(); + } + } else if (ctxCons instanceof HybObjCtxConstructor) { + if (ctftt1.hybridFormula1() || ctftt2.hybridFormula2()) { + return caller.context(); + } + } else { + throw new RuntimeException( + "unsupported context constructor for tunneling: " + ctxCons.getClass()); + } + return ctxCons.constructCtx(caller, receiverNode, callSite, target); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/dd/data-driven/README.md b/sootup.qilin/src/main/java/qilin/pta/toolkits/dd/data-driven/README.md new file mode 100644 index 00000000000..e64ef7008f9 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/dd/data-driven/README.md @@ -0,0 +1,5 @@ +# References + +1. [Data-driven context-sensitivity for points-to analysis](https://dl.acm.org/doi/abs/10.1145/3133924) + +2. [Artifact Link](https://github.com/kupl/Data-Driven-Pointsto-Analysis) \ No newline at end of file diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/dd/tunneling/README.md b/sootup.qilin/src/main/java/qilin/pta/toolkits/dd/tunneling/README.md new file mode 100644 index 00000000000..8dac19e44fb --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/dd/tunneling/README.md @@ -0,0 +1,5 @@ +# References + +1. [Precise and Scalable Points-to Analysis via Data-Driven Context Tunneling](https://dl.acm.org/doi/10.1145/3276510) + +2. [Artifact Link](https://dl.acm.org/do/10.1145/3276931/full/) \ No newline at end of file diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/CollectionHeuristic.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/CollectionHeuristic.java new file mode 100644 index 00000000000..b151600dc37 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/CollectionHeuristic.java @@ -0,0 +1,219 @@ +package qilin.pta.toolkits.debloaterx; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import qilin.core.PTA; +import qilin.core.pag.AllocNode; +import qilin.core.pag.FieldRefNode; +import qilin.core.pag.LocalVarNode; +import qilin.core.pag.MethodPAG; +import qilin.core.pag.Node; +import qilin.core.pag.PAG; +import qilin.core.pag.SparkField; +import qilin.util.PTAUtils; +import qilin.util.queue.QueueReader; +import sootup.core.model.SootClass; +import sootup.core.model.SootMethod; +import sootup.core.types.ArrayType; +import sootup.core.types.ClassType; +import sootup.core.types.Type; + +/* + * This is a much simpler scheme for finding context-dependent objects. + * This file does not belong to any part of DebloaterX. + * (1) a container type is a class which has a `java.lang.Object` field or a container type field, + * or it implements `java.util.Collection` or is nested in a class implementing `java.util.Collection`. + * (2) an object is context-dependent if it is of a container type. + * */ +public class CollectionHeuristic { + protected final PTA pta; + protected final PAG pag; + + protected final Map> t2Fields = new ConcurrentHashMap<>(); + + protected final Set containerType = ConcurrentHashMap.newKeySet(); + + protected final Set ctxDepHeaps = ConcurrentHashMap.newKeySet(); + + public Set getCtxDepHeaps() { + return ctxDepHeaps; + } + + public CollectionHeuristic(PTA pta) { + this.pta = pta; + this.pag = pta.getPag(); + } + + private void buildHeapFieldsMappingIn(SootMethod method) { + MethodPAG srcmpag = pag.getMethodPAG(method); + Set stores = new HashSet<>(); + Set loads = new HashSet<>(); + QueueReader reader = srcmpag.getInternalReader().clone(); + while (reader.hasNext()) { + Node from = reader.next(), to = reader.next(); + if (from instanceof LocalVarNode) { + if (to instanceof FieldRefNode) { + FieldRefNode frn = (FieldRefNode) to; + stores.add(frn); + } + } else if (from instanceof FieldRefNode) { + FieldRefNode frn = (FieldRefNode) from; + loads.add(frn); + } + } + // handle STORE + for (FieldRefNode frn : stores) { + LocalVarNode storeBase = (LocalVarNode) frn.getBase(); + SparkField field = frn.getField(); + for (AllocNode heap : pta.reachingObjects(storeBase).toCIPointsToSet().toCollection()) { + t2Fields.computeIfAbsent(heap.getType(), k -> ConcurrentHashMap.newKeySet()).add(field); + } + } + // handle LOAD + for (FieldRefNode frn : loads) { + LocalVarNode loadBase = (LocalVarNode) frn.getBase(); + SparkField field = frn.getField(); + for (AllocNode heap : pta.reachingObjects(loadBase).toCIPointsToSet().toCollection()) { + t2Fields.computeIfAbsent(heap.getType(), k -> ConcurrentHashMap.newKeySet()).add(field); + } + } + } + + private void buildHeapFieldsMapping() { + pta.getNakedReachableMethods().stream() + .filter(PTAUtils::hasBody) + .forEach(this::buildHeapFieldsMappingIn); + } + + private boolean isImplementingCollection(SootClass sc) { + Set allInterfaces = new HashSet<>(sc.getInterfaces()); + while (sc.hasSuperclass()) { + ClassType classType = sc.getSuperclass().get(); + sc = pta.getView().getClass(classType).get(); + allInterfaces.addAll(sc.getInterfaces()); + } + // interface may also have super class + Set worklist = new HashSet<>(); + for (ClassType tmp : allInterfaces) { + SootClass msc = pta.getView().getClass(tmp).get(); + worklist.add(msc); + while (msc.hasSuperclass()) { + ClassType superType = msc.getSuperclass().get(); + SootClass superClazz = pta.getView().getClass(superType).get(); + if (!superClazz.isInterface()) break; + worklist.add(superClazz); + msc = superClazz; + } + } + boolean flag = false; + for (SootClass interf : worklist) { + if (interf.getType() == PTAUtils.getClassType("java.util.Collection") + // || interf.getType() == RefType.v("java.util.Map") + ) { + flag = true; + } + } + return flag; + } + + private boolean isNestedInClassImplementCollection(SootClass sc) { + if (!sc.isInnerClass()) { + return false; + } + ClassType outerType = sc.getOuterClass().get(); + SootClass outer = pta.getView().getClass(outerType).get(); + if (isImplementingCollection(outer)) { + return true; + } + return isNestedInClassImplementCollection(outer); + } + + private void computeContainerTypes() { + for (Type type : t2Fields.keySet()) { + if (type instanceof ClassType) { + ClassType refType = (ClassType) type; + SootClass sc = pta.getView().getClass(refType).get(); + if (isImplementingCollection(sc) || isNestedInClassImplementCollection(sc)) { + containerType.add(type); + } else { + for (SparkField sf : t2Fields.get(type)) { + if (sf.getType() == PTAUtils.getClassType("java.lang.Object")) { + containerType.add(type); + break; + } + } + } + } else if (type instanceof ArrayType) { + ArrayType at = (ArrayType) type; + if (at.getBaseType() == PTAUtils.getClassType("java.lang.Object")) { + containerType.add(at); + } + } else { + System.out.println(type); + } + } + // build a mapping from any field type to their containing type. + Map> ft2t = new HashMap<>(); + for (Type type : t2Fields.keySet()) { + if (type instanceof ClassType) { + for (SparkField sf : t2Fields.get(type)) { + Type sft = sf.getType(); + if (sft instanceof ArrayType) { + ArrayType at = (ArrayType) sft; + sft = at.getBaseType(); + } + ft2t.computeIfAbsent(sft, k -> new HashSet<>()).add(type); + } + } else if (type instanceof ArrayType) { + ArrayType at = (ArrayType) type; + ft2t.computeIfAbsent(at.getBaseType(), k -> new HashSet<>()).add(type); + } + } + // find more container types by checking whether a type has a field of a container type. + Set newlyFound = new HashSet<>(); + containerType.addAll( + ft2t.getOrDefault(PTAUtils.getClassType("java.lang.Object"), Collections.emptySet())); + for (Type t1 : containerType) { + for (Type t2 : ft2t.getOrDefault(t1, Collections.emptySet())) { + if (!containerType.contains(t2)) { + newlyFound.add(t2); + } + } + } + while (!newlyFound.isEmpty()) { + containerType.addAll(newlyFound); + Set tmp = new HashSet<>(); + for (Type t1 : newlyFound) { + for (Type t2 : ft2t.getOrDefault(t1, Collections.emptySet())) { + if (!containerType.contains(t2)) { + tmp.add(t2); + } + } + } + newlyFound.clear(); + newlyFound.addAll(tmp); + } + System.out.println("#ContainerType:" + containerType.size()); + } + + private void computeContextDependentObjects() { + for (AllocNode heap : pag.getAllocNodes()) { + if (containerType.contains(heap.getType())) { + ctxDepHeaps.add(heap); + } + } + System.out.println("#OBJECTS:" + pag.getAllocNodes().size()); + System.out.println("#CS:" + ctxDepHeaps.size()); + System.out.println("#CI:" + (pag.getAllocNodes().size() - ctxDepHeaps.size())); + } + + public void run() { + buildHeapFieldsMapping(); + computeContainerTypes(); + computeContextDependentObjects(); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/ContainerFinder.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/ContainerFinder.java new file mode 100644 index 00000000000..82a535d8c56 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/ContainerFinder.java @@ -0,0 +1,141 @@ +package qilin.pta.toolkits.debloaterx; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +import qilin.core.PTA; +import qilin.core.pag.AllocNode; +import qilin.core.pag.ArrayElement; +import qilin.core.pag.PAG; +import qilin.core.pag.SparkField; +import qilin.util.Stopwatch; +import sootup.core.jimple.basic.Value; +import sootup.core.jimple.common.constant.IntConstant; +import sootup.core.jimple.common.expr.JNewArrayExpr; +import sootup.core.types.ArrayType; +import sootup.core.types.ClassType; +import sootup.core.types.Type; + +/* + * implementing rules for identifying container objects as shown in Figure 8 in the paper. + * */ +public class ContainerFinder { + protected final PTA pta; + protected final PAG pag; + private final Set notcontainers = ConcurrentHashMap.newKeySet(); + private final Map> containers = new ConcurrentHashMap<>(); + + private final XUtility utility; + + public ContainerFinder(PTA pta, XUtility utility) { + this.pta = pta; + this.pag = pta.getPag(); + this.utility = utility; + } + + /* + * classify objects into primitive containers, normal containers and notcontainers. + * */ + public void run() { + Stopwatch s1 = Stopwatch.newAndStart("pre-containerFinder"); + Set remainObjs = new HashSet<>(); + for (AllocNode heap : pag.getAllocNodes()) { + Type type = heap.getType(); + if (type instanceof ArrayType) { + ArrayType at = (ArrayType) type; + JNewArrayExpr nae = (JNewArrayExpr) heap.getNewExpr(); + Value vl = nae.getSize(); + if (utility.isCoarseType(at) + && (!(vl instanceof IntConstant) || ((IntConstant) vl).getValue() != 0)) { + containers.computeIfAbsent(heap, k -> new HashSet<>()).add(ArrayElement.v()); + } else { + notcontainers.add(heap); + } + } else if (type instanceof ClassType) { + ClassType refType = (ClassType) type; + if (utility.isCoarseType(refType) && heap.getMethod() != null) { + remainObjs.add(heap); + } else { + notcontainers.add(heap); + } + } else { + throw new RuntimeException("invalid type for " + heap); + } + } + s1.stop(); + System.out.println(s1); + Stopwatch s2 = Stopwatch.newAndStart("mid-containerFinder"); + remainObjs.parallelStream().forEach(utility::getHCQ); + s2.stop(); + System.out.println(s2); + Stopwatch s3 = Stopwatch.newAndStart("remain-containerFinder"); + remainObjs + .parallelStream() + .forEach( + heap -> { + Set fields = utility.getFields(heap); + fields = + fields.stream() + .filter(f -> utility.isCoarseType(f.getType())) + .collect(Collectors.toSet()); + HeapContainerQuery hcq = utility.getHCQ(heap); + for (SparkField field : fields) { + // check in + boolean hasIn = hasNonThisStoreOnField(heap, field, hcq); + if (!hasIn) { + continue; + } + // check out + boolean hasOut = hasNonThisLoadFromField(heap, field, hcq); + if (hasOut) { + containers.computeIfAbsent(heap, k -> new HashSet<>()).add(field); + } + } + if (!containers.containsKey(heap)) { + notcontainers.add(heap); + } + }); + s3.stop(); + System.out.println(s3); + System.out.println("#ObjectsNotAContainer:" + notcontainers.size()); + System.out.println("#Container:" + containers.size()); + } + + private boolean hasNonThisStoreOnField(AllocNode heap, SparkField field, HeapContainerQuery hcq) { + if (utility.hasNonThisStoreOnField(heap, field)) { + return true; + } + return hcq.hasParamsStoredInto(field); + } + + private boolean hasNonThisLoadFromField( + AllocNode heap, SparkField field, HeapContainerQuery hcq) { + if (utility.hasNonThisLoadFromField(heap, field)) { + return true; + } + return hcq.hasOutMethodsWithRetOrParamValueFrom(field); + } + + public boolean isAContainer(AllocNode heap) { + if (this.containers.containsKey(heap)) { + return true; + } else if (heap.getMethod() + .getSignature() + .toString() + .startsWith( + "") + || heap.getMethod() + .getSignature() + .toString() + .startsWith( + "")) { + // We will remove such hacks in the future. The noise of [java.lang.String] types introduced + // by the resolving of reflection of + // Array.newInstance makes the whole analysis imprecise. Qilin's reflection mechanism causes + // this. One potential solution is + // not to resolve reflection for these two methods. + return true; + } + return false; + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/DebloaterX.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/DebloaterX.java new file mode 100644 index 00000000000..9ac67d83008 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/DebloaterX.java @@ -0,0 +1,209 @@ +package qilin.pta.toolkits.debloaterx; + +import com.google.common.collect.Sets; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import qilin.core.PTA; +import qilin.core.pag.*; +import qilin.core.sets.PointsToSet; +import qilin.util.PTAUtils; +import sootup.core.model.SootMethod; +import sootup.core.types.ClassType; +import sootup.core.types.ReferenceType; +import sootup.core.types.Type; + +/* + * A container usage pattern-based approach to identifying context-independent objects for context debloating. + * Corresponding to Algorithm 1 in the paper. + * */ +public class DebloaterX { + private final PTA pta; + private final PAG pag; + private final ContainerFinder containerFinder; + private final XUtility utility; + + public DebloaterX(PTA pta) { + this.pta = pta; + this.pag = pta.getPag(); + this.utility = new XUtility(pta); + this.containerFinder = new ContainerFinder(pta, utility); + this.containerFinder.run(); + } + + /* + * identifying factory-created containers (Fig 11 in the paper) + * */ + private boolean isAFactoryCreatedContainer(AllocNode heap, IntraFlowAnalysis mpag) { + SootMethod method = heap.getMethod(); + if (method.isStatic()) { + Type type = method.getReturnType(); + if (!(type instanceof ReferenceType)) { + return false; + } + MethodPAG methodPag = pag.getMethodPAG(method); + VarNode mRet = methodPag.nodeFactory().caseRet(); + if (pta.reachingObjects(mRet).toCIPointsToSet().toCollection().contains(heap)) { + return mpag.isDirectlyReturnedHeap(heap); + } + } + return false; + } + + /* + * identifying container wrappers (Fig 12 in the paper) + * */ + private boolean isAContainerWrapper(AllocNode heap, IntraFlowAnalysis mpag) { + SootMethod method = heap.getMethod(); + if (method.isStatic()) { + return false; + } + Type type = method.getReturnType(); + if (!(type instanceof ReferenceType)) { + return false; + } + MethodPAG methodPag = pag.getMethodPAG(method); + VarNode mRet = methodPag.nodeFactory().caseRet(); + PointsToSet pts = pta.reachingObjects(mRet); + Collection ptsSet = pts.toCIPointsToSet().toCollection(); + if (ptsSet.contains(heap)) { + if (mpag.isDirectlyReturnedHeap(heap)) { + return mpag.isContentFromParam(heap); + } + } + return false; + } + + /* + * identify inner containers (Fig 9 in the paper). + * */ + private boolean isAnInnerContainer(AllocNode heap, IntraFlowAnalysis mpag) { + SootMethod method = heap.getMethod(); + if (method.isStatic()) { + return false; + } + Set fields = mpag.retrieveStoreFields(heap); + if (fields.isEmpty()) { + return false; + } + Set objects = this.utility.getReceiverObjects(method); + for (AllocNode revobj : objects) { + if (revobj.getType() instanceof ClassType) { + HeapContainerQuery hcq = this.utility.getHCQ(revobj); + for (SparkField field : fields) { + if (hcq.isCSField(field)) { + return true; + } + } + } + } + return false; + } + + protected final Set ctxDepHeaps = ConcurrentHashMap.newKeySet(); + protected final Set containerFactory = ConcurrentHashMap.newKeySet(); + protected final Set containerWrapper = ConcurrentHashMap.newKeySet(); + protected final Set innerContainer = ConcurrentHashMap.newKeySet(); + + /* + * Implementing Step 3 of Algorithm 1 (in the paper): finding context-dependent objects according to container-usage patterns + * */ + public void run() { + Map> m2o = new HashMap<>(); + for (AllocNode heap : pag.getAllocNodes()) { + SootMethod method = heap.getMethod(); + if (method == null || PTAUtils.isStaticInitializer(method)) { + continue; + } + m2o.computeIfAbsent(method, k -> new HashSet<>()).add(heap); + } + m2o.keySet() + .parallelStream() + .forEach( + method -> { + IntraFlowAnalysis ifa = new IntraFlowAnalysis(utility, method); + for (AllocNode heap : m2o.get(method)) { + if (!this.containerFinder.isAContainer(heap)) { + continue; + } + if (isAFactoryCreatedContainer(heap, ifa)) { + containerFactory.add(heap); + ctxDepHeaps.add(heap); + } + if (isAContainerWrapper(heap, ifa)) { + containerWrapper.add(heap); + ctxDepHeaps.add(heap); + } + if (isAnInnerContainer(heap, ifa)) { + innerContainer.add(heap); + ctxDepHeaps.add(heap); + } + } + }); + System.out.println("#OBJECTS:" + pag.getAllocNodes().size()); + System.out.println("#CS:" + ctxDepHeaps.size()); + System.out.println("#CI:" + (pag.getAllocNodes().size() - ctxDepHeaps.size())); + System.out.println("#ContainerFactory:" + containerFactory.size()); + System.out.println("#ContainerWrapper:" + containerWrapper.size()); + System.out.println("#InnerContainer:" + innerContainer.size()); + { + // for drawn venn3 figure. + int onlyInFactory = + Sets.difference(Sets.difference(containerFactory, containerWrapper), innerContainer) + .size(); + int onlyInWrapper = + Sets.difference(Sets.difference(containerWrapper, containerFactory), innerContainer) + .size(); + int onlyInInner = + Sets.difference(Sets.difference(innerContainer, containerWrapper), containerFactory) + .size(); + int inAll = + Sets.intersection(Sets.intersection(innerContainer, containerWrapper), containerFactory) + .size(); + int onlyInFactoryAndWrapper = + Sets.difference(Sets.intersection(containerFactory, containerWrapper), innerContainer) + .size(); + int onlyInFactoryAndInner = + Sets.difference(Sets.intersection(containerFactory, innerContainer), containerWrapper) + .size(); + int onlyInWrapperAndInner = + Sets.difference(Sets.intersection(containerWrapper, innerContainer), containerFactory) + .size(); + System.out.println("#onlyInFactory:" + onlyInFactory); + System.out.println("#onlyInWrapper:" + onlyInWrapper); + System.out.println("#onlyInInner:" + onlyInInner); + System.out.println("#inAll:" + inAll); + System.out.println("#onlyInFactoryAndWrapper:" + onlyInFactoryAndWrapper); + System.out.println("#onlyInFactoryAndInner:" + onlyInFactoryAndInner); + System.out.println("#onlyInWrapperAndInner:" + onlyInWrapperAndInner); + System.out.println( + "#SUM:" + + (onlyInFactory + + onlyInWrapper + + onlyInInner + + inAll + + onlyInFactoryAndWrapper + + onlyInFactoryAndInner + + onlyInWrapperAndInner)); + System.out.println( + "venn3(subsets = (" + + onlyInFactory + + "," + + onlyInWrapper + + "," + + onlyInFactoryAndWrapper + + "," + + onlyInInner + + "," + + onlyInFactoryAndInner + + "," + + onlyInWrapperAndInner + + ", " + + inAll + + "))"); + } + } + + public Set getCtxDepHeaps() { + return ctxDepHeaps; + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/Edge.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/Edge.java new file mode 100644 index 00000000000..e9bde4cf46e --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/Edge.java @@ -0,0 +1,18 @@ +package qilin.pta.toolkits.debloaterx; + +import qilin.core.pag.Node; +import qilin.core.pag.SparkField; + +public class Edge { + Node from; + Node to; + SparkField field; + EdgeKind kind; + + Edge(Node from, Node to, SparkField f, EdgeKind kind) { + this.from = from; + this.to = to; + this.field = f; + this.kind = kind; + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/EdgeKind.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/EdgeKind.java new file mode 100644 index 00000000000..944943bef34 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/EdgeKind.java @@ -0,0 +1,27 @@ +package qilin.pta.toolkits.debloaterx; + +public enum EdgeKind { + // "I" means inverse. + NEW, + INEW, + // the following two used for normal assign, cast, and inter-procedural assign for invocations + // with "this" as the base variable. + // no global assignments are included. + ASSIGN, + IASSIGN, + // the following four used only for normal Java store and load. + LOAD, + ILOAD, + STORE, + ISTORE, + // the following four model parameter passing for non-this invocation. + CLOAD, + ICLOAD, + CSTORE, + ICSTORE, + // the following three are self-loops. + THIS, + ITHIS, + PARAM, + RETURN, +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/HeapContainerQuery.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/HeapContainerQuery.java new file mode 100644 index 00000000000..bae42fe6c19 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/HeapContainerQuery.java @@ -0,0 +1,78 @@ +package qilin.pta.toolkits.debloaterx; + +import java.util.*; +import java.util.stream.Collectors; +import qilin.core.builder.MethodNodeFactory; +import qilin.core.pag.*; +import qilin.util.PTAUtils; +import sootup.core.model.SootMethod; +import sootup.core.types.ReferenceType; + +public class HeapContainerQuery { + private final PAG pag; + private final XUtility utility; + private final Set invokedMs; + private final Set params; + private final InterFlowAnalysis interfa; + private final AllocNode heap; + + public HeapContainerQuery(XUtility utility, AllocNode heap) { + this.utility = utility; + this.pag = utility.getPta().getPag(); + this.heap = heap; + this.invokedMs = utility.getInvokedMethods(heap); + this.interfa = utility.getInterFlowAnalysis(); + this.params = getParameters(); + } + + /* computes input parameters for the class of refType */ + private Set getParameters() { + Set ret = new HashSet<>(); + for (SootMethod m : invokedMs) { + MethodNodeFactory mthdNF = pag.getMethodPAG(m).nodeFactory(); + for (int i = 0; i < m.getParameterCount(); ++i) { + if (m.getParameterType(i) instanceof ReferenceType + && !PTAUtils.isPrimitiveArrayType(m.getParameterType(i))) { + LocalVarNode param = (LocalVarNode) mthdNF.caseParm(i); + ret.add(param); + } + } + ret.add((LocalVarNode) mthdNF.caseThis()); + } + return ret; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /* APIs for query */ + public boolean hasParamsStoredInto(SparkField field) { + Set tmp = interfa.getParamsStoredInto(field); + tmp = tmp.stream().filter(this.params::contains).collect(Collectors.toSet()); + return !tmp.isEmpty(); + } + + public Set getInParamsToCSFields() { + Set fields = utility.getFields(heap); + Set ret = new HashSet<>(); + for (SparkField field : fields) { + if (isCSField(field)) { + ret.addAll(interfa.getParamsStoredInto(field)); + } + } + ret = ret.stream().filter(this.params::contains).collect(Collectors.toSet()); + return ret; + } + + public boolean hasOutMethodsWithRetOrParamValueFrom(SparkField field) { + Set tmp = interfa.getOutMethodsWithRetOrParamValueFrom(field); + tmp = tmp.stream().filter(this.invokedMs::contains).collect(Collectors.toSet()); + return !tmp.isEmpty(); + } + + public boolean isCSField(SparkField field) { + boolean hasIn = utility.hasNonThisStoreOnField(this.heap, field) || hasParamsStoredInto(field); + boolean hasOut = + utility.hasNonThisLoadFromField(this.heap, field) + || hasOutMethodsWithRetOrParamValueFrom(field); + return hasIn && hasOut; + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/InterFlowAnalysis.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/InterFlowAnalysis.java new file mode 100644 index 00000000000..49b413f4011 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/InterFlowAnalysis.java @@ -0,0 +1,249 @@ +package qilin.pta.toolkits.debloaterx; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +import qilin.core.pag.LocalVarNode; +import qilin.core.pag.Node; +import qilin.core.pag.SparkField; +import qilin.util.Pair; +import qilin.util.queue.UniqueQueue; +import sootup.core.model.SootMethod; + +public class InterFlowAnalysis { + protected final XUtility utility; + protected final XPAG xpag; + /* records the reachability info: given a field F, (1) which params could be saved into F + * (2) F could flows to which method's param or return node. + */ + protected final Map> field2InParams = new ConcurrentHashMap<>(); + protected final Map> field2OutParams = new ConcurrentHashMap<>(); + + public InterFlowAnalysis(XUtility utility) { + this.utility = utility; + this.xpag = utility.getXpag(); + reachabilityAnalysis(); + } + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /* reachability analysis: fill content in field2InParams and field2Outs + * This is the implementation of Algorithm 2 in the paper. + * */ + public void reachabilityAnalysis() { + Set fields = utility.getFields(); + fields + .parallelStream() + .forEach( + field -> { + // compute the value of field could be loaded to which outmethods. + Set retOrParams = runDFAandCollect(field, false); + if (!retOrParams.isEmpty()) { + field2OutParams + .computeIfAbsent(field, k -> ConcurrentHashMap.newKeySet()) + .addAll(retOrParams); + } + // compute the params could be stored into the specified field. + Set params = runDFAandCollect(field, true); + if (!params.isEmpty()) { + field2InParams + .computeIfAbsent(field, k -> ConcurrentHashMap.newKeySet()) + .addAll(params); + } + }); + } + + /* + * When isInflow = true, check whether the value in param could be stored into field. + * Otherwise, check whether the value in field could be loaded to any method's return value or params. + * */ + private Set runDFAandCollect(SparkField field, boolean isInflow) { + Set ret = new HashSet<>(); + Map> state2nodes = new HashMap<>(); + Queue> queue = new UniqueQueue<>(); + queue.add(new Pair<>(xpag.getDummyThis(), State.THIS)); + while (!queue.isEmpty()) { + Pair front = queue.poll(); + if (front.getSecond() == State.End) { + if (front.getFirst() instanceof LocalVarNode) { + LocalVarNode lvn = (LocalVarNode) front.getFirst(); + ret.add(lvn); + } + } + // visit the node and state. + visit(front, state2nodes); + Set> nexts = getNextNodeStates(front, field, isInflow); + for (Pair nodeState : nexts) { + if (!isVisited(nodeState, state2nodes)) { + queue.add(nodeState); + } + } + } + return ret; + } + + private Set> getNextNodeStates( + Pair nodeState, SparkField field, boolean in) { + Node node = nodeState.getFirst(); + State state = nodeState.getSecond(); + Set> ret = new HashSet<>(); + for (Edge edge : xpag.getOutEdges(node)) { + boolean mathched = edge.field != null && edge.field.equals(field); + State nextState; + if (in) { + nextState = nextStateForIn(state, edge.kind, mathched); + } else { + nextState = nextStateForOut(state, edge.kind, mathched); + } + if (nextState != State.Error) { + ret.add(new Pair<>(edge.to, nextState)); + } + } + return ret; + } + + /* + * This method describes another automaton and its transition function for checking whether the field value could be + * loaded to any method's return. + * Correspoing to the automaton given in Fig 7c in the paper. + * */ + private State nextStateForOut(State currState, EdgeKind kind, boolean fieldMatch) { + switch (currState) { + case THIS: + { + if (kind == EdgeKind.ITHIS) { + return State.ThisAlias; + } + } + case ThisAlias: + { + if (kind == EdgeKind.ASSIGN) { + return State.ThisAlias; + } else if (kind == EdgeKind.LOAD && fieldMatch) { + return State.VPlus; + } + } + case VPlus: + { + if (kind == EdgeKind.ASSIGN || kind == EdgeKind.LOAD || kind == EdgeKind.CLOAD) { + return State.VPlus; + } else if (kind == EdgeKind.RETURN) { + return State.End; + } else if (kind == EdgeKind.CSTORE + || kind == EdgeKind.STORE + || kind == EdgeKind.ICSTORE + || kind == EdgeKind.ISTORE) { + // kind == EdgeKind.ICSTORE || kind == EdgeKind.ISTORE can be removed. + return State.VMinus; + } + } + case VMinus: + { + if (kind == EdgeKind.IASSIGN || kind == EdgeKind.ILOAD || kind == EdgeKind.ICLOAD) { + return State.VMinus; + } else if (kind == EdgeKind.INEW) { + return State.O; + } else if (kind == EdgeKind.PARAM) { + return State.End; + } + } + case O: + { + if (kind == EdgeKind.NEW) { + return State.VPlus; + } + } + } + return State.Error; + } + + private void visit(Pair nodeState, Map> state2nodes) { + Node node = nodeState.getFirst(); + State state = nodeState.getSecond(); + state2nodes.computeIfAbsent(state, k -> new HashSet<>()).add(node); + } + + private boolean isVisited(Pair nodeState, Map> state2nodes) { + Node node = nodeState.getFirst(); + State state = nodeState.getSecond(); + Set nodes = state2nodes.getOrDefault(state, Collections.emptySet()); + return nodes.contains(node); + } + + /* + * This method describes an automaton and its transition function for checking whether the value could be stored + * into any field. + * Correspoing to the automaton given in Fig 7b in the paper. + * */ + private State nextStateForIn(State currState, EdgeKind kind, boolean fieldMatch) { + switch (currState) { + case O: + { + if (kind == EdgeKind.NEW) { + return State.VPlus; + } + } + case THIS: + { + if (kind == EdgeKind.ITHIS) { + return State.ThisAlias; + } + } + case VPlus: + { + if (kind == EdgeKind.ASSIGN || kind == EdgeKind.LOAD || kind == EdgeKind.CLOAD) { + return State.VPlus; + } else if (kind == EdgeKind.ISTORE + || kind == EdgeKind.STORE + || kind == EdgeKind.ICSTORE + || kind == EdgeKind.CSTORE) { + // kind == EdgeKind.STORE || kind == EdgeKind.ICSTORE can be deleted due to not occured + // in + // the real world. + return State.VMinus; + } + } + case VMinus: + { + if (kind == EdgeKind.IASSIGN || kind == EdgeKind.ILOAD || kind == EdgeKind.ICLOAD) { + return State.VMinus; + } else if (kind == EdgeKind.INEW) { + return State.O; + } else if (kind == EdgeKind.PARAM) { + return State.End; + } + } + case ThisAlias: + { + if (kind == EdgeKind.ASSIGN) { + return State.ThisAlias; + } else if (kind == EdgeKind.ISTORE) { + if (fieldMatch) { + return State.VMinus; + } + } else if (kind == EdgeKind.LOAD) { + if (fieldMatch) { + return State.VPlus; + } + } + } + } + return State.Error; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /* APIs for query */ + public Set getParamsStoredInto(SparkField field) { + return this.field2InParams.getOrDefault(field, Collections.emptySet()); + } + + public Set getOutMethodsWithRetOrParamValueFrom(SparkField field) { + return field2OutParams.getOrDefault(field, Collections.emptySet()).stream() + .map(LocalVarNode::getMethod) + .collect(Collectors.toSet()); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/IntraFlowAnalysis.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/IntraFlowAnalysis.java new file mode 100644 index 00000000000..e6fa96f8c29 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/IntraFlowAnalysis.java @@ -0,0 +1,320 @@ +package qilin.pta.toolkits.debloaterx; + +import java.util.HashSet; +import java.util.Queue; +import java.util.Set; +import qilin.core.builder.MethodNodeFactory; +import qilin.core.pag.*; +import qilin.util.PTAUtils; +import qilin.util.Pair; +import qilin.util.queue.QueueReader; +import qilin.util.queue.UniqueQueue; +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.basic.Value; +import sootup.core.jimple.common.constant.NullConstant; +import sootup.core.jimple.common.expr.AbstractInstanceInvokeExpr; +import sootup.core.jimple.common.expr.AbstractInvokeExpr; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.SootMethod; +import sootup.core.signatures.MethodSubSignature; +import sootup.core.types.ArrayType; +import sootup.core.types.ClassType; +import sootup.core.types.ReferenceType; +import sootup.core.types.Type; + +/* + * Implementation of Algorithm 3 and Fig 10 in the paper. + * */ +public class IntraFlowAnalysis { + private final PAG pag; + private final XUtility utility; + private final SootMethod method; + protected final XPAG xpag; + protected final Set params = new HashSet<>(); + + public IntraFlowAnalysis(XUtility utility, SootMethod method) { + this.utility = utility; + this.pag = utility.getPta().getPag(); + this.method = method; + this.xpag = utility.getXpag(); + collectParams(); + } + + protected void collectParams() { + MethodPAG srcmpag = pag.getMethodPAG(method); + MethodNodeFactory srcnf = srcmpag.nodeFactory(); + VarNode thisNode = srcnf.caseThis(); + // handle parameters + for (int i = 0; i < method.getParameterCount(); ++i) { + if (method.getParameterType(i) instanceof ReferenceType + && !PTAUtils.isPrimitiveArrayType(method.getParameterType(i))) { + LocalVarNode param = (LocalVarNode) srcnf.caseParm(i); + this.params.add(param); + } + } + this.params.add((LocalVarNode) thisNode); + } + + ////////////////////////////////////////////////////////////////////////////////// + /* + * x = y = z = ... = node; + * */ + Set epsilon(Node node) { + Queue queue = new UniqueQueue<>(); + for (Edge edge : xpag.getOutEdges(node)) { + queue.add(edge.to); + } + Set visit = new HashSet<>(); + while (!queue.isEmpty()) { + Node front = queue.poll(); + visit.add(front); + for (Edge edge : xpag.getOutEdges(front)) { + if (edge.kind == EdgeKind.ASSIGN && !visit.contains(edge.to)) { + queue.add(edge.to); + } + } + } + return visit; + } + + /* + * t = new T; + * x = ... = t; + * return x; + * */ + public boolean isDirectlyReturnedHeap(AllocNode heap) { + Set visit = epsilon(heap); + boolean flag = false; + for (Node node : visit) { + if (node instanceof LocalVarNode) { + LocalVarNode lvn = (LocalVarNode) node; + if (lvn.isReturn()) { + flag = true; + } + } + } + return flag; + } + + public boolean isContentFromParam(AllocNode heap) { + Type heapType = heap.getType(); + if (heapType instanceof ClassType) { + return isInstanceObjectContentFromParam(heap); + } else { + return isArrayContentFromParam(heap); + } + } + + /* + * return true iff heap.f comes from any parameter of heap.getMethod(). + * */ + private boolean isInstanceObjectContentFromParam(AllocNode heap) { + Set paramInArgs = collectParamInArguments(heap); + if (paramInArgs.isEmpty()) { + return false; + } + Queue queue = new UniqueQueue<>(); + Set visited = new HashSet<>(); + queue.addAll(params); + while (!queue.isEmpty()) { + Node front = queue.poll(); + if (paramInArgs.contains(front)) { + return true; + } + visited.add(front); + for (Edge edge : xpag.getOutEdges(front)) { + if (edge.kind == EdgeKind.ASSIGN + || edge.kind == EdgeKind.CLOAD + || edge.kind == EdgeKind.LOAD) { + if (!visited.contains(edge.to)) { + queue.add(edge.to); + } + } + } + } + return false; + } + + private Set collectParamInArguments(AllocNode heap) { + ClassType type = (ClassType) heap.getType(); + Set x = epsilon(heap); + Set ret = new HashSet<>(); + HeapContainerQuery hcq = this.utility.getHCQ(heap); + Set inParams = hcq.getInParamsToCSFields(); + MethodPAG srcmpag = pag.getMethodPAG(method); + for (final Stmt s : srcmpag.getInvokeStmts()) { + AbstractInvokeExpr ie = s.getInvokeExpr(); + if (!(ie instanceof AbstractInstanceInvokeExpr)) { + continue; + } + AbstractInstanceInvokeExpr iie = (AbstractInstanceInvokeExpr) ie; + Local base = iie.getBase(); + LocalVarNode receiver = pag.findLocalVarNode(method, base, base.getType()); + if (!x.contains(receiver)) { + continue; + } + int numArgs = ie.getArgCount(); + Value[] args = new Value[numArgs]; + for (int i = 0; i < numArgs; i++) { + Value arg = ie.getArg(i); + if (!(arg.getType() instanceof ReferenceType) || arg instanceof NullConstant) { + continue; + } + args[i] = arg; + } + MethodSubSignature subSig = iie.getMethodSignature().getSubSignature(); + VirtualCallSite virtualCallSite = + new VirtualCallSite( + receiver, + s, + new ContextMethod(method, utility.getPta().emptyContext()), + iie, + subSig, + qilin.core.builder.callgraph.Edge.ieToKind(iie)); + QueueReader targets = pag.getCgb().dispatch(type, virtualCallSite); + while (targets.hasNext()) { + SootMethod target = targets.next(); + MethodPAG tgtmpag = pag.getMethodPAG(target); + MethodNodeFactory tgtnf = tgtmpag.nodeFactory(); + int numParms = target.getParameterCount(); + if (numParms != numArgs) { + System.out.println(target); + } + for (int i = 0; i < numParms; i++) { + if (target.getParameterType(i) instanceof ReferenceType) { + if (args[i] != null) { + ValNode argNode = pag.findValNode(args[i], method); + if (argNode instanceof LocalVarNode) { + LocalVarNode lvn = (LocalVarNode) argNode; + LocalVarNode param = (LocalVarNode) tgtnf.caseParm(i); + if (inParams.contains(param)) { + ret.add(lvn); + } + } + } + } + } + } + } + return ret; + } + + /* + * x = new T[] + * x[i] = param.*; + * */ + private boolean isArrayContentFromParam(AllocNode heap) { + if (!(heap.getType() instanceof ArrayType)) { + return false; + } + Set x = epsilon(heap); + Queue queue = new UniqueQueue<>(); + Set visited = new HashSet<>(); + queue.addAll(params); + while (!queue.isEmpty()) { + Node front = queue.poll(); + visited.add(front); + for (Edge edge : xpag.getOutEdges(front)) { + if (edge.kind == EdgeKind.ASSIGN + || edge.kind == EdgeKind.CLOAD + || edge.kind == EdgeKind.LOAD) { + if (!visited.contains(edge.to)) { + queue.add(edge.to); + } + } + if (edge.kind == EdgeKind.STORE) { + if (x.contains(edge.to)) { + return true; + } + } + } + } + return false; + } + + private State nextState(State currState, EdgeKind kind) { + switch (currState) { + case O: + { + if (kind == EdgeKind.NEW) { + return State.VPlus; + } + } + case VPlus: + { + if (kind == EdgeKind.ASSIGN) { + return State.VPlus; + } else if (kind == EdgeKind.STORE) { + return State.VMinus; + } + } + case VMinus: + { + if (kind == EdgeKind.IASSIGN) { + return State.VMinus; + } else if (kind == EdgeKind.ILOAD) { + return State.VMinus; + } else if (kind == EdgeKind.INEW) { + return State.O; + } + } + } + return State.Error; + } + + /* + * implementation of nextNodeStates in Algorithm 3 in the paper. It also encodes Fig 10 in the paper. + * */ + private Set> getNextNodeStates( + Pair nodeState, Set thisAlias, Set stFields) { + Node node = nodeState.getFirst(); + State state = nodeState.getSecond(); + Set> ret = new HashSet<>(); + for (Edge edge : xpag.getOutEdges(node)) { + State nextState = nextState(state, edge.kind); + if (nextState != State.Error) { + if (edge.kind == EdgeKind.STORE && thisAlias.contains(edge.to)) { + Type type = edge.field.getType(); + if (!utility.isCoarseType(type)) { + continue; + } + stFields.add(edge.field); + } else if (edge.kind == EdgeKind.ILOAD && thisAlias.contains(edge.to)) { + Type type = edge.field.getType(); + if (!utility.isCoarseType(type)) { + continue; + } + stFields.add(edge.field); + } else { + ret.add(new Pair<>(edge.to, nextState)); + } + } + } + return ret; + } + + /* + * Implementation of Algorithm 3 in the paper. + * */ + public Set retrieveStoreFields(AllocNode heap) { + Set ret = new HashSet<>(); + MethodPAG srcmpag = pag.getMethodPAG(method); + MethodNodeFactory srcnf = srcmpag.nodeFactory(); + VarNode thisNode = srcnf.caseThis(); + Set thisAlias = epsilon(thisNode); + Queue> queue = new UniqueQueue<>(); + Set> visited = new HashSet<>(); + queue.add(new Pair<>(heap, State.O)); + while (!queue.isEmpty()) { + Pair front = queue.poll(); + visited.add(front); + Set> nextStates = getNextNodeStates(front, thisAlias, ret); + for (Pair nextState : nextStates) { + if (!visited.contains(nextState)) { + queue.add(nextState); + } + } + } + return ret; + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/State.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/State.java new file mode 100644 index 00000000000..15ee218a98d --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/State.java @@ -0,0 +1,12 @@ +package qilin.pta.toolkits.debloaterx; + +public enum State { + P, + VPlus, + VMinus, + O, + ThisAlias, + THIS, + End, + Error +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/XPAG.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/XPAG.java new file mode 100644 index 00000000000..5969fa91ec6 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/XPAG.java @@ -0,0 +1,293 @@ +package qilin.pta.toolkits.debloaterx; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import qilin.core.PTA; +import qilin.core.builder.MethodNodeFactory; +import qilin.core.pag.*; +import qilin.util.PTAUtils; +import qilin.util.queue.QueueReader; +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.basic.Value; +import sootup.core.jimple.common.constant.NullConstant; +import sootup.core.jimple.common.expr.AbstractInstanceInvokeExpr; +import sootup.core.jimple.common.expr.AbstractInvokeExpr; +import sootup.core.jimple.common.expr.JSpecialInvokeExpr; +import sootup.core.jimple.common.expr.JStaticInvokeExpr; +import sootup.core.jimple.common.stmt.JAssignStmt; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.SootMethod; +import sootup.core.types.ReferenceType; + +public class XPAG { + /* record nodes and edges in the graph */ + protected final Map> outEdges = new ConcurrentHashMap<>(); + protected final PTA pta; + protected final PAG pag; + private final LocalVarNode dummyThis; + protected final XUtility utility; + + public XPAG(PTA pta, XUtility utility) { + this.pta = pta; + this.pag = pta.getPag(); + this.utility = utility; + this.dummyThis = new LocalVarNode("DUMMYTHIS", PTAUtils.getClassType("java.lang.Object"), null); + buildGraph(pta.getNakedReachableMethods()); + } + + /* + * Constructing the XPAG for a program (Figure 5 in the paper) + * */ + protected void buildGraph(Collection reachables) { + // add edges in each method + reachables.parallelStream().forEach(this::buildInternal); + } + + protected void buildInternal(SootMethod method) { + buildInternalWithInline(method); + } + + protected void buildInternalWithInline(SootMethod method) { + MethodPAG srcmpag = pag.getMethodPAG(method); + MethodNodeFactory srcnf = srcmpag.nodeFactory(); + VarNode thisNode = srcnf.caseThis(); + // add special ``this'' edge + addThisEdge((LocalVarNode) thisNode); + // add normal edges: NEW, ASSIGN, LOAD, STORE + QueueReader reader = srcmpag.getInternalReader().clone(); + while (reader.hasNext()) { + Node from = reader.next(), to = reader.next(); + if (from instanceof LocalVarNode) { + if (to instanceof LocalVarNode) { + this.addAssignEdge((LocalVarNode) from, (LocalVarNode) to); + } else if (to instanceof FieldRefNode) { + FieldRefNode fr = (FieldRefNode) to; + this.addStoreEdge((LocalVarNode) from, (LocalVarNode) fr.getBase(), fr.getField()); + } // local-global + + } else if (from instanceof AllocNode) { + if (to instanceof LocalVarNode) { + this.addNewEdge((AllocNode) from, (LocalVarNode) to); + } // GlobalVarNode + } else if (from instanceof FieldRefNode) { + FieldRefNode fr = (FieldRefNode) from; + this.addLoadEdge((LocalVarNode) fr.getBase(), (LocalVarNode) to, fr.getField()); + } // global-local + } + // handle call statements. + for (final Stmt s : srcmpag.getInvokeStmts()) { + AbstractInvokeExpr ie = s.getInvokeExpr(); + int numArgs = ie.getArgCount(); + Value[] args = new Value[numArgs]; + for (int i = 0; i < numArgs; i++) { + Value arg = ie.getArg(i); + if (!(arg.getType() instanceof ReferenceType) || arg instanceof NullConstant) continue; + args[i] = arg; + } + LocalVarNode retDest = null; + if (s instanceof JAssignStmt) { + Value dest = ((JAssignStmt) s).getLeftOp(); + if (dest.getType() instanceof ReferenceType) { + retDest = pag.findLocalVarNode(method, dest, dest.getType()); + } + } + if (ie instanceof AbstractInstanceInvokeExpr) { + AbstractInstanceInvokeExpr iie = (AbstractInstanceInvokeExpr) ie; + Local base = iie.getBase(); + LocalVarNode receiver = pag.findLocalVarNode(method, base, base.getType()); + if (iie instanceof JSpecialInvokeExpr) { + JSpecialInvokeExpr sie = (JSpecialInvokeExpr) iie; + Optional optMethod = + pta.getView().getMethod(sie.getMethodSignature()); + if (optMethod.isPresent()) { + SootMethod target = optMethod.get(); + inline(method, s, target); + } else { + /* instance call with non-this base variable are modeled as in Eagle/Turner. */ + modelVirtualCall(method, numArgs, args, receiver, retDest); + } + } else { + /* instance call with non-this base variable are modeled as in Eagle/Turner. */ + modelVirtualCall(method, numArgs, args, receiver, retDest); + } + } else { + if (ie instanceof JStaticInvokeExpr) { + JStaticInvokeExpr sie = (JStaticInvokeExpr) ie; + Optional optMethod = + pta.getView().getMethod(sie.getMethodSignature()); + if (optMethod.isPresent()) { + SootMethod target = optMethod.get(); + inline(method, s, target); + } + } + } + } + // handle parameters. + for (int i = 0; i < method.getParameterCount(); ++i) { + if (method.getParameterType(i) instanceof ReferenceType + && !PTAUtils.isPrimitiveArrayType(method.getParameterType(i))) { + LocalVarNode param = (LocalVarNode) srcnf.caseParm(i); + addParamEdge(param); + } + } + // treat this as a special parameter. + if (!method.isStatic()) { + addParamEdge((LocalVarNode) srcnf.caseThis()); + } + // handle returns + if (method.getReturnType() instanceof ReferenceType + && !PTAUtils.isPrimitiveArrayType(method.getReturnType())) { + addReturnEdge((LocalVarNode) srcnf.caseRet()); + } + } + + private void modelVirtualCall( + SootMethod method, int numArgs, Value[] args, LocalVarNode receiver, LocalVarNode retDest) { + for (int i = 0; i < numArgs; i++) { + if (args[i] == null) { + continue; + } + ValNode argNode = pag.findValNode(args[i], method); + if (argNode instanceof LocalVarNode) { + addCStoreEdge((LocalVarNode) argNode, receiver); + } + } + if (retDest != null) { + addCLoadEdge(receiver, retDest); + } + addCStoreEdge(receiver, receiver); + } + + private void inline(SootMethod srcMethod, Stmt invokeStmt, SootMethod tgtMethod) { + AbstractInvokeExpr ie = invokeStmt.getInvokeExpr(); + int numArgs = ie.getArgCount(); + Value[] args = new Value[numArgs]; + for (int i = 0; i < numArgs; i++) { + Value arg = ie.getArg(i); + if (!(arg.getType() instanceof ReferenceType) || arg instanceof NullConstant) continue; + args[i] = arg; + } + LocalVarNode retDest = null; + if (invokeStmt instanceof JAssignStmt) { + Value dest = ((JAssignStmt) invokeStmt).getLeftOp(); + if (dest.getType() instanceof ReferenceType) { + retDest = pag.findLocalVarNode(tgtMethod, dest, dest.getType()); + } + } + LocalVarNode receiver = null; + if (ie instanceof AbstractInstanceInvokeExpr) { + AbstractInstanceInvokeExpr iie = (AbstractInstanceInvokeExpr) ie; + Local base = iie.getBase(); + receiver = pag.findLocalVarNode(tgtMethod, base, base.getType()); + } + MethodPAG mpag = pag.getMethodPAG(tgtMethod); + MethodNodeFactory nodeFactory = mpag.nodeFactory(); + if (numArgs != tgtMethod.getParameterCount()) { + return; + } + // handle parameters + for (int i = 0; i < tgtMethod.getParameterCount(); ++i) { + if (args[i] != null + && tgtMethod.getParameterType(i) instanceof ReferenceType + && !PTAUtils.isPrimitiveArrayType(tgtMethod.getParameterType(i))) { + LocalVarNode param = (LocalVarNode) nodeFactory.caseParm(i); + ValNode argVal = pag.findValNode(args[i], srcMethod); + if (argVal instanceof LocalVarNode) { + LocalVarNode argNode = (LocalVarNode) argVal; + addAssignEdge(argNode, param); + } + } + } + // handle return node + if (retDest != null + && tgtMethod.getReturnType() instanceof ReferenceType + && !PTAUtils.isPrimitiveArrayType(tgtMethod.getReturnType())) { + addAssignEdge((LocalVarNode) nodeFactory.caseRet(), retDest); + } + // handle this node + if (receiver != null) { + addAssignEdge(receiver, (LocalVarNode) nodeFactory.caseThis()); + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + protected void addNormalEdge(Edge edge) { + outEdges.computeIfAbsent(edge.from, k -> ConcurrentHashMap.newKeySet()).add(edge); + } + + /* methods for adding normal edges: NEW, ASSIGN, STORE, LOAD. */ + protected void addNewEdge(AllocNode from, LocalVarNode to) { + // skip merged heaps. + if (from.getMethod() == null && !(from instanceof ConstantNode)) { + return; + } + Edge newEdge = new Edge(from, to, null, EdgeKind.NEW); + addNormalEdge(newEdge); + Edge iNewEdge = new Edge(to, from, null, EdgeKind.INEW); + addNormalEdge(iNewEdge); + } + + protected void addAssignEdge(LocalVarNode from, LocalVarNode to) { + Edge assignEdge = new Edge(from, to, null, EdgeKind.ASSIGN); + addNormalEdge(assignEdge); + Edge iAssignEdge = new Edge(to, from, null, EdgeKind.IASSIGN); + addNormalEdge(iAssignEdge); + } + + protected void addStoreEdge(LocalVarNode from, LocalVarNode base, SparkField field) { + Edge storeEdge = new Edge(from, base, field, EdgeKind.STORE); + addNormalEdge(storeEdge); + Edge iStoreEdge = new Edge(base, from, field, EdgeKind.ISTORE); + addNormalEdge(iStoreEdge); + } + + protected void addLoadEdge(LocalVarNode base, LocalVarNode to, SparkField field) { + Edge loadEdge = new Edge(base, to, field, EdgeKind.LOAD); + addNormalEdge(loadEdge); + Edge iLoadEdge = new Edge(to, base, field, EdgeKind.ILOAD); + addNormalEdge(iLoadEdge); + } + + /* methods for adding inter-procedural edges at non-this invocation site */ + protected void addCStoreEdge(LocalVarNode from, LocalVarNode base) { + Edge cstoreEdge = new Edge(from, base, null, EdgeKind.CSTORE); + addNormalEdge(cstoreEdge); + Edge iCstoreEdge = new Edge(base, from, null, EdgeKind.ICSTORE); + addNormalEdge(iCstoreEdge); + } + + protected void addCLoadEdge(LocalVarNode base, LocalVarNode to) { + Edge cLoadEdge = new Edge(base, to, null, EdgeKind.CLOAD); + addNormalEdge(cLoadEdge); + Edge iCloadEdge = new Edge(to, base, null, EdgeKind.ICLOAD); + addNormalEdge(iCloadEdge); + } + + /* this is a special edge */ + protected void addThisEdge(LocalVarNode thisNode) { + Edge thisEdge = new Edge(thisNode, dummyThis, null, EdgeKind.THIS); + addNormalEdge(thisEdge); + Edge ithisEdge = new Edge(dummyThis, thisNode, null, EdgeKind.ITHIS); + addNormalEdge(ithisEdge); + } + + /* methods for adding self-loop edges */ + protected void addParamEdge(LocalVarNode param) { + Edge paramEdge = new Edge(param, param, null, EdgeKind.PARAM); + addNormalEdge(paramEdge); + } + + protected void addReturnEdge(LocalVarNode ret) { + Edge retEdge = new Edge(ret, ret, null, EdgeKind.RETURN); + addNormalEdge(retEdge); + } + + public Set getOutEdges(Node node) { + return this.outEdges.getOrDefault(node, Collections.emptySet()); + } + + public LocalVarNode getDummyThis() { + return dummyThis; + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/XUtility.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/XUtility.java new file mode 100644 index 00000000000..e1f9bdf5170 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/XUtility.java @@ -0,0 +1,344 @@ +package qilin.pta.toolkits.debloaterx; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +import qilin.core.PTA; +import qilin.core.builder.MethodNodeFactory; +import qilin.core.builder.callgraph.Edge; +import qilin.core.builder.callgraph.OnFlyCallGraph; +import qilin.core.pag.*; +import qilin.util.PTAUtils; +import qilin.util.Stopwatch; +import qilin.util.queue.QueueReader; +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.common.expr.AbstractInstanceInvokeExpr; +import sootup.core.jimple.common.expr.AbstractInvokeExpr; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.SootClass; +import sootup.core.model.SootField; +import sootup.core.model.SootMethod; +import sootup.core.signatures.MethodSubSignature; +import sootup.core.types.ArrayType; +import sootup.core.types.ClassType; +import sootup.core.types.Type; + +public class XUtility { + protected final PTA pta; + protected final PAG pag; + + protected final Map o2HCQ = new ConcurrentHashMap<>(); + /* records objects and their instance fields */ + protected final Map> o2Fields = new ConcurrentHashMap<>(); + protected final Map> t2Fields = new ConcurrentHashMap<>(); + protected final Map>> o2nonThisFStores = + new ConcurrentHashMap<>(); + protected final Map>> t2nonThisFStores = + new ConcurrentHashMap<>(); + protected final Map>> o2nonThisFLoads = + new ConcurrentHashMap<>(); + protected final Map>> t2nonThisFLoads = + new ConcurrentHashMap<>(); + + /* records the methods and their receiver objects and types */ + protected final Map> o2InvokedMethods = new HashMap<>(); + protected final Map> t2InvokedMethods = new HashMap<>(); + protected final Map> m2receiverObjects = new HashMap<>(); + + protected final Set rawOrPolyTypes = new HashSet<>(); + protected final XPAG xpag; + protected final InterFlowAnalysis interfa; + + public XUtility(PTA pta) { + this.pta = pta; + this.pag = pta.getPag(); + Stopwatch stopwatch = Stopwatch.newAndStart("HackUtility construction"); + buildHeapFieldsMapping(); + buildHeapMethodsMapping(); + computeRawOrPolyTypes(); + this.xpag = new XPAG(pta, this); + this.interfa = new InterFlowAnalysis(this); + stopwatch.stop(); + System.out.println(stopwatch); + } + + public PTA getPta() { + return pta; + } + + public XPAG getXpag() { + return xpag; + } + + public InterFlowAnalysis getInterFlowAnalysis() { + return interfa; + } + + private boolean isImpreciseType(Type type) { + if (type == PTAUtils.getClassType("java.lang.Object")) { + return true; + } + if (type instanceof ClassType) { + ClassType refType = (ClassType) type; + SootClass sc = pta.getView().getClass(refType).get(); + return sc.isAbstract() || sc.isInterface() || refType.getClassName().startsWith("Abstract"); + } + return false; + } + + /* Implemnting the rules for defining coarse types (Figure 6 in the paper) */ + public boolean isCoarseType(Type type) { + if (type instanceof ArrayType) { + ArrayType at = (ArrayType) type; + type = at.getElementType(); + } + return isImpreciseType(type) || rawOrPolyTypes().contains(type); + } + + private void computeRawOrPolyTypes() { + Set types = new HashSet<>(); + for (AllocNode heap : pag.getAllocNodes()) { + Type type = heap.getType(); + if (type instanceof ArrayType) { + ArrayType at = (ArrayType) type; + Type et = at.getElementType(); + if (isImpreciseType(et)) { + rawOrPolyTypes.add(et); + } else { + types.add(et); + } + } else { + for (SparkField field : getFields(heap)) { + Type ft = field.getType(); + if (ft instanceof ArrayType) { + ArrayType fat = (ArrayType) ft; + ft = fat.getElementType(); + } + if (isImpreciseType(ft)) { + rawOrPolyTypes.add(ft); + rawOrPolyTypes.add(type); + } else { + types.add(type); + types.add(ft); + } + } + } + } + boolean continueUpdating = true; + while (continueUpdating) { + continueUpdating = false; + for (Type type : types) { + for (SparkField field : getFields(type)) { + Type ft = field.getType(); + if (isCoarseType(ft)) { + if (rawOrPolyTypes.add(type)) { + continueUpdating = true; + } + } + } + } + } + } + + private Set rawOrPolyTypes() { + return rawOrPolyTypes; + } + + /* record objects and their fields */ + private void buildHeapFieldsMappingIn(SootMethod method) { + MethodPAG srcmpag = pag.getMethodPAG(method); + MethodNodeFactory srcnf = srcmpag.nodeFactory(); + LocalVarNode thisRef = (LocalVarNode) srcnf.caseThis(); + Set stores = new HashSet<>(); + Set loads = new HashSet<>(); + Set thisAliases = new HashSet<>(); + thisAliases.add(thisRef); + QueueReader reader = srcmpag.getInternalReader().clone(); + while (reader.hasNext()) { + Node from = reader.next(), to = reader.next(); + if (from instanceof LocalVarNode) { + if (to instanceof FieldRefNode) { + FieldRefNode frn = (FieldRefNode) to; + stores.add(frn); + } + if (thisAliases.contains(from) && to instanceof LocalVarNode) { + thisAliases.add(to); + } + } else if (from instanceof FieldRefNode) { + FieldRefNode frn = (FieldRefNode) from; + loads.add(frn); + } + } + // handle STORE + for (FieldRefNode frn : stores) { + LocalVarNode storeBase = (LocalVarNode) frn.getBase(); + SparkField field = frn.getField(); + boolean isNonthisBase = !thisAliases.contains(storeBase); + for (AllocNode heap : pta.reachingObjects(storeBase).toCIPointsToSet().toCollection()) { + o2Fields.computeIfAbsent(heap, k -> ConcurrentHashMap.newKeySet()).add(field); + t2Fields.computeIfAbsent(heap.getType(), k -> ConcurrentHashMap.newKeySet()).add(field); + if (isNonthisBase) { + Map> f2bs = + o2nonThisFStores.computeIfAbsent(heap, k -> new ConcurrentHashMap<>()); + f2bs.computeIfAbsent(field, k -> ConcurrentHashMap.newKeySet()).add(storeBase); + Map> f2bsx = + t2nonThisFStores.computeIfAbsent(heap.getType(), k -> new ConcurrentHashMap<>()); + f2bsx.computeIfAbsent(field, k -> ConcurrentHashMap.newKeySet()).add(storeBase); + } + } + } + // handle LOAD + for (FieldRefNode frn : loads) { + LocalVarNode loadBase = (LocalVarNode) frn.getBase(); + SparkField field = frn.getField(); + boolean isNonthisBase = !thisAliases.contains(loadBase); + for (AllocNode heap : pta.reachingObjects(loadBase).toCIPointsToSet().toCollection()) { + o2Fields.computeIfAbsent(heap, k -> ConcurrentHashMap.newKeySet()).add(field); + t2Fields.computeIfAbsent(heap.getType(), k -> ConcurrentHashMap.newKeySet()).add(field); + if (isNonthisBase) { + Map> f2bs = + o2nonThisFLoads.computeIfAbsent(heap, k -> new ConcurrentHashMap<>()); + f2bs.computeIfAbsent(field, k -> ConcurrentHashMap.newKeySet()).add(loadBase); + Map> f2bsx = + t2nonThisFLoads.computeIfAbsent(heap.getType(), k -> new ConcurrentHashMap<>()); + f2bsx.computeIfAbsent(field, k -> ConcurrentHashMap.newKeySet()).add(loadBase); + } + } + } + } + + private void buildHeapFieldsMapping() { + pta.getNakedReachableMethods().stream() + .filter(PTAUtils::hasBody) + .forEach(this::buildHeapFieldsMappingIn); + } + + /* records objects (together with their types) and their invoked methods */ + private void buildHeapMethodsMapping() { + OnFlyCallGraph callgraph = pta.getCallGraph(); + // collect virtual callsites. + Set vcallsites = new HashSet<>(); + for (Edge edge : callgraph) { + SootMethod srcM = edge.src(); + SootMethod tgtM = edge.tgt(); + if (tgtM.isStatic() || !PTAUtils.hasBody(tgtM)) { + continue; + } + final Stmt s = edge.srcStmt(); + AbstractInvokeExpr ie = s.getInvokeExpr(); + if (ie instanceof AbstractInstanceInvokeExpr) { + AbstractInstanceInvokeExpr iie = (AbstractInstanceInvokeExpr) ie; + Local base = iie.getBase(); + LocalVarNode receiver = pag.findLocalVarNode(srcM, base, base.getType()); + MethodSubSignature subSig = iie.getMethodSignature().getSubSignature(); + VirtualCallSite virtualCallSite = + new VirtualCallSite( + receiver, + s, + new ContextMethod(srcM, pta.emptyContext()), + iie, + subSig, + qilin.core.builder.callgraph.Edge.ieToKind(iie)); + vcallsites.add(virtualCallSite); + } else { + throw new RuntimeException("ie could not be of " + ie.getClass()); + } + } + // foreach virtualcallsite, we build mapping from their receiver objects. + for (VirtualCallSite vcallsite : vcallsites) { + AbstractInstanceInvokeExpr iie = vcallsite.iie(); + Local base = iie.getBase(); + LocalVarNode receiver = + pag.findLocalVarNode(vcallsite.container().method(), base, base.getType()); + for (AllocNode heap : pta.reachingObjects(receiver).toCIPointsToSet().toCollection()) { + QueueReader reader = pta.getCgb().dispatch(heap.getType(), vcallsite); + while (reader.hasNext()) { + SootMethod tgtM = reader.next(); + m2receiverObjects.computeIfAbsent(tgtM, k -> new HashSet<>()).add(heap); + o2InvokedMethods.computeIfAbsent(heap, k -> new HashSet<>()).add(tgtM); + t2InvokedMethods.computeIfAbsent(heap.getType(), k -> new HashSet<>()).add(tgtM); + } + } + } + } + + // Below are public APIs for access. + + public Set getReceiverObjects(SootMethod method) { + return m2receiverObjects.getOrDefault(method, Collections.emptySet()); + } + + public Set getInvokedMethods(AllocNode heap) { + return o2InvokedMethods.getOrDefault(heap, Collections.emptySet()); + } + + /* get or create TPAG for a given type */ + public HeapContainerQuery getHCQ(AllocNode heap) { + HeapContainerQuery hcq = o2HCQ.get(heap); + if (hcq == null) { + hcq = new HeapContainerQuery(this, heap); + o2HCQ.put(heap, hcq); + } + return hcq; + } + + public Set getFields() { + Set tmp = + this.o2Fields.values().stream().flatMap(Collection::stream).collect(Collectors.toSet()); + Set ret = new HashSet<>(); + for (SparkField field : tmp) { + Type type = field.getType(); + if (isCoarseType(type)) { + ret.add(field); + } + } + return ret; + } + + public Set getFields(AllocNode heap) { + return this.o2Fields.getOrDefault(heap, Collections.emptySet()); + } + + public Set getFields(Type type) { + if (type instanceof ClassType) { + ClassType refType = (ClassType) type; + Set ret = this.t2Fields.get(refType); + if (ret != null) { + return ret; + } else { + ret = this.t2Fields.computeIfAbsent(refType, k -> new HashSet<>()); + for (AllocNode heap : this.o2Fields.keySet()) { + if (PTAUtils.canStoreType(pta.getView(), heap.getType(), refType)) { + for (SparkField sparkField : this.o2Fields.get(heap)) { + if (sparkField instanceof Field) { + Field f = (Field) sparkField; + SootField sf = f.getField(); + Type declType = sf.getDeclaringClassType(); + if (PTAUtils.canStoreType(pta.getView(), type, declType)) { + ret.add(sparkField); + } + } else { + throw new RuntimeException(sparkField + ";" + sparkField.getClass()); + } + } + } + } + return ret; + } + } else { + return Collections.emptySet(); + } + } + + public boolean hasNonThisStoreOnField(AllocNode heap, SparkField field) { + Map> field2bases = + this.o2nonThisFStores.getOrDefault(heap, Collections.emptyMap()); + return field2bases.containsKey(field); + } + + public boolean hasNonThisLoadFromField(AllocNode heap, SparkField field) { + Map> field2bases = + this.o2nonThisFLoads.getOrDefault(heap, Collections.emptyMap()); + return field2bases.containsKey(field); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/eagle/BNode.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/eagle/BNode.java new file mode 100644 index 00000000000..fad5c4a71d3 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/eagle/BNode.java @@ -0,0 +1,59 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.eagle; + +import qilin.core.pag.AllocNode; +import qilin.util.PTAUtils; + +/** Original Graph Node(sparkNode) expanded bidirectinally */ +public class BNode { + public Object sparkNode; + public Boolean forward; + public int level; + public Boolean cs; + + public boolean entryCS() { + if (this.cs) return false; + return this.cs = true; + } + + public BNode(Object origin, Boolean forward) { + this.sparkNode = origin; + this.forward = forward; + this.cs = false; + this.level = 0; + } + + public Object getIR() { + return PTAUtils.getIR(sparkNode); + } + + boolean isHeapPlus() { + return sparkNode instanceof AllocNode && this.forward; + } + + boolean isHeapMinus() { + return sparkNode instanceof AllocNode && !this.forward; + } + + @Override + public String toString() { + return sparkNode + "," + forward; + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/eagle/Eagle.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/eagle/Eagle.java new file mode 100644 index 00000000000..8fb78dbbd30 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/eagle/Eagle.java @@ -0,0 +1,407 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.eagle; + +import java.util.*; +import java.util.stream.Collectors; +import qilin.core.PTA; +import qilin.core.PointsToAnalysis; +import qilin.core.builder.MethodNodeFactory; +import qilin.core.builder.callgraph.Edge; +import qilin.core.builder.callgraph.OnFlyCallGraph; +import qilin.core.pag.*; +import qilin.core.sets.PointsToSet; +import qilin.util.PTAUtils; +import qilin.util.Util; +import qilin.util.queue.QueueReader; +import qilin.util.queue.UniqueQueue; +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.basic.Value; +import sootup.core.jimple.common.constant.NullConstant; +import sootup.core.jimple.common.expr.AbstractInstanceInvokeExpr; +import sootup.core.jimple.common.expr.AbstractInvokeExpr; +import sootup.core.jimple.common.stmt.JAssignStmt; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.SootMethod; +import sootup.core.types.ReferenceType; + +// implementation of Eagle (OOPSLA'19). +public class Eagle { + protected Map> sparkNode2BNode = new HashMap<>(); + // fields for storage + public Set allocs = new HashSet<>(); + public Set allocIs = new HashSet<>(); + public Map> outEdges = new HashMap<>(); + protected Map> balancedOutEdges = new HashMap<>(); + + private int new_count = 0; + private int assign_count = 0; + protected int store_count = 0; + private int load_count = 0; + private int balance_count = 0; + private int hstore_count = 0; + private int hload_count = 0; + private int total_nodes_count = 0; + private int total_edges_count = 0; + + public void dumpCount() { + System.out.println("#NEW:" + new_count); + System.out.println("#ASSIGN:" + assign_count); + System.out.println("#STORE:" + store_count); + System.out.println("#LOAD:" + load_count); + System.out.println("#HSTORE:" + hstore_count); + System.out.println("#HLOAD:" + hload_count); + System.out.println("#BALANCE:" + balance_count); + } + + public Collection getNodes() { + return sparkNode2BNode.values().stream() + .flatMap(subMap -> subMap.values().stream()) + .collect(Collectors.toSet()); + } + + public Collection getSparkNodes() { + return sparkNode2BNode.values().stream() + .flatMap(subMap -> subMap.values().stream()) + .map(gn -> gn.sparkNode) + .collect(Collectors.toSet()); + } + + public BNode getBNode(Object origin, Boolean forward) { + Map subMap = sparkNode2BNode.computeIfAbsent(origin, k -> new HashMap<>()); + if (subMap.containsKey(forward)) { + return subMap.get(forward); + } else { + BNode ret = new BNode(origin, forward); + subMap.put(forward, ret); + total_nodes_count++; + return ret; + } + } + + protected void addNormalEdge(BNode from, BNode to) { + Set m = outEdges.computeIfAbsent(from, k -> new HashSet<>()); + m.add(to); + total_edges_count++; + } + + public boolean addBalancedEdge(BNode from, BNode to) { + boolean ret = Util.addToMap(balancedOutEdges, from, to); + balance_count++; + total_edges_count++; + return ret; + } + + public void addNewEdge(AllocNode from, LocalVarNode to) { + BNode fromE = getBNode(from, true), toE = getBNode(to, true); + addNormalEdge(fromE, toE); + BNode toEI = getBNode(to, false), fromEI = getBNode(from, false); + addNormalEdge(toEI, fromEI); + + new_count++; + allocs.add(fromE); + allocIs.add(fromEI); + } + + public void addAssignEdge(LocalVarNode from, LocalVarNode to) { + BNode fromE = getBNode(from, true), toE = getBNode(to, true); + addNormalEdge(fromE, toE); + BNode toEI = getBNode(to, false), fromEI = getBNode(from, false); + addNormalEdge(toEI, fromEI); + + assign_count++; + } + + public void addStoreEdge(LocalVarNode from, LocalVarNode base) { + BNode fromE = getBNode(from, true), baseEI = getBNode(base, false); + addNormalEdge(fromE, baseEI); + BNode baseE = getBNode(base, true), fromEI = getBNode(from, false); + addNormalEdge(baseE, fromEI); + + store_count++; + } + + public void addLoadEdge(LocalVarNode base, LocalVarNode to) { + BNode baseE = getBNode(base, true), toE = getBNode(to, true); + addNormalEdge(baseE, toE); + BNode toEI = getBNode(to, false), baseEI = getBNode(base, false); + addNormalEdge(toEI, baseEI); + + load_count++; + } + + public void addHstoreEdge(Object from, AllocNode baseObj) { + BNode fromE = getBNode(from, true), baseObjE = getBNode(baseObj, true); + addNormalEdge(fromE, baseObjE); + BNode baseObjEI = getBNode(baseObj, false), fromEI = getBNode(from, false); + addNormalEdge(baseObjEI, fromEI); + allocIs.add(baseObjEI); + allocs.add(baseObjE); + hstore_count++; + } + + public void addHloadEdge(AllocNode baseObj, Object to) { + BNode baseObjEI = getBNode(baseObj, false), toE = getBNode(to, true); + addNormalEdge(baseObjEI, toE); + BNode toEI = getBNode(to, false), baseObjE = getBNode(baseObj, true); + addNormalEdge(toEI, baseObjE); + allocIs.add(baseObjEI); + allocs.add(baseObjE); + hload_count++; + } + + public int totalEdgesCount() { + return total_edges_count; + } + + public int totalNodesCount() { + return total_nodes_count; + } + + public Set getAllOutEdges(BNode node) { + Set ret = new HashSet<>(getOutEdges(node)); + if (balancedOutEdges.containsKey(node)) { + ret.addAll(balancedOutEdges.get(node)); + } + return ret; + } + + public Collection getOutEdges(BNode node) { + return outEdges.getOrDefault(node, Collections.emptySet()); + } + + public boolean reachValidReceiverObject(BNode from, BNode to) { + BNode fromEI = getBNode(to.sparkNode, false); + if (from.sparkNode instanceof Field || from.sparkNode instanceof ArrayElement) { + return getOutEdges(fromEI).contains(from); + } + return true; + } + + // eagle propagate + protected boolean enterCS(BNode node) { + return node.entryCS(); + } + + public Map contxtLengthAnalysis() { + Queue workList = new UniqueQueue<>(); + Set matchedObjects = new HashSet<>(); + // start from all "parameter/field" node + for (BNode heapNode : allocIs) { + for (BNode dst : getOutEdges(heapNode)) { + dst.cs = true; + workList.add(dst); + } + } + while (!workList.isEmpty()) { + BNode node = workList.poll(); + for (BNode dst : getAllOutEdges(node)) { + if (dst.isHeapPlus() && !node.isHeapMinus()) { + if (reachValidReceiverObject(node, dst)) { + if (matchedObjects.add(dst.sparkNode)) { // add balanced edges + BNode fromEI = getBNode(dst.sparkNode, false); + addBalancedEdge(fromEI, dst, workList); + } + } + } else { + if (enterCS(dst)) { + workList.add(dst); + } + } + } + } + Map ret = new HashMap<>(); + getSparkNodes() + .forEach( + sparkNode -> { + BNode node = getBNode(sparkNode, true); + BNode nodeInv = getBNode(sparkNode, false); + ret.put(sparkNode, node.cs && nodeInv.cs ? 1 : 0); + }); + return ret; + } + + protected void addBalancedEdge(BNode from, BNode to, Queue workList) { + if (addBalancedEdge(from, to)) { + if (from.cs) { + workList.add(from); // add src of the balanced edges to worklist + } + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + protected void addParamEdges( + AllocNode o, + LocalVarNode thisRef, + LocalVarNode[] parms, + LocalVarNode mret, + LocalVarNode mThrow) { + this.addHloadEdge(o, thisRef); + for (VarNode parm : parms) { + if (parm != null) { + this.addHloadEdge(o, parm); + } + } + if (mret != null) { + this.addHstoreEdge(mret, o); + } + if (mThrow != null) { + this.addHstoreEdge(mThrow, o); + } + } + + public void buildGraph(PTA prePTA) { + PAG prePAG = prePTA.getPag(); + // calculate points-to set for "This" pointer in each static method. + Map> pts = PTAUtils.calcStaticThisPTS(prePTA); + + OnFlyCallGraph callGraph = prePTA.getCallGraph(); + for (SootMethod method : prePTA.getNakedReachableMethods()) { + if (!PTAUtils.hasBody(method)) { + continue; + } + MethodPAG srcmpag = prePAG.getMethodPAG(method); + MethodNodeFactory srcnf = srcmpag.nodeFactory(); + LocalVarNode thisRef = (LocalVarNode) srcnf.caseThis(); + // add local edges + if (PTAUtils.isFakeMainMethod(method)) { + // special treatment for fake main + this.addNewEdge(prePTA.getRootNode(), thisRef); + } + QueueReader reader = srcmpag.getInternalReader().clone(); + while (reader.hasNext()) { + Node from = reader.next(), to = reader.next(); + if (from instanceof LocalVarNode) { + if (to instanceof LocalVarNode) + this.addAssignEdge((LocalVarNode) from, (LocalVarNode) to); + else if (to instanceof FieldRefNode) { + FieldRefNode fr = (FieldRefNode) to; + this.addStoreEdge((LocalVarNode) from, (LocalVarNode) fr.getBase()); + } // local-global + + } else if (from instanceof AllocNode) { + if (to instanceof LocalVarNode) { + this.addNewEdge((AllocNode) from, (LocalVarNode) to); + } // GlobalVarNode + } else if (from instanceof FieldRefNode) { + FieldRefNode fr = (FieldRefNode) from; + this.addLoadEdge((LocalVarNode) fr.getBase(), (LocalVarNode) to); + } // global-local + } + // add exception edges that added dynamically during the pre-analysis. + srcmpag + .getExceptionEdges() + .forEach( + (k, vs) -> { + for (Node v : vs) { + this.addAssignEdge((LocalVarNode) k, (LocalVarNode) v); + } + }); + // add para edges + int numParms = method.getParameterCount(); + LocalVarNode[] parms = new LocalVarNode[numParms]; + for (int i = 0; i < numParms; i++) { + if (method.getParameterType(i) instanceof ReferenceType) { + parms[i] = (LocalVarNode) srcnf.caseParm(i); + } + } + LocalVarNode mret = + method.getReturnType() instanceof ReferenceType ? (LocalVarNode) srcnf.caseRet() : null; + LocalVarNode throwFinal = + prePAG.findLocalVarNode( + method, + new Parm(method, PointsToAnalysis.THROW_NODE), + PTAUtils.getClassType("java.lang.Throwable")); + if (method.isStatic()) { + pts.getOrDefault(thisRef, Collections.emptySet()) + .forEach( + a -> { + addParamEdges(a, thisRef, parms, mret, throwFinal); + }); + } else { + PointsToSet thisPts = prePTA.reachingObjects(thisRef).toCIPointsToSet(); + for (Iterator it = thisPts.iterator(); it.hasNext(); ) { + AllocNode n = it.next(); + addParamEdges(n, thisRef, parms, mret, throwFinal); + } + } + + // add invoke edges + for (final Stmt s : srcmpag.getInvokeStmts()) { + AbstractInvokeExpr ie = s.getInvokeExpr(); + int numArgs = ie.getArgCount(); + Value[] args = new Value[numArgs]; + for (int i = 0; i < numArgs; i++) { + Value arg = ie.getArg(i); + if (!(arg.getType() instanceof ReferenceType) || arg instanceof NullConstant) continue; + args[i] = arg; + } + LocalVarNode retDest = null; + if (s instanceof JAssignStmt) { + Value dest = ((JAssignStmt) s).getLeftOp(); + if (dest.getType() instanceof ReferenceType) { + retDest = prePAG.findLocalVarNode(method, dest, dest.getType()); + } + } + LocalVarNode receiver; + if (ie instanceof AbstractInstanceInvokeExpr) { + AbstractInstanceInvokeExpr iie = (AbstractInstanceInvokeExpr) ie; + Local base = iie.getBase(); + receiver = prePAG.findLocalVarNode(method, base, base.getType()); + } else { + // static call + receiver = thisRef; + } + for (Iterator it = callGraph.edgesOutOf(s); it.hasNext(); ) { + Edge e = it.next(); + SootMethod tgtmtd = e.tgt(); + for (int i = 0; i < numArgs; i++) { + if (args[i] == null || !(tgtmtd.getParameterType(i) instanceof ReferenceType)) continue; + ValNode argNode = prePAG.findValNode(args[i], method); + if (argNode instanceof LocalVarNode) { + this.addStoreEdge((LocalVarNode) argNode, receiver); + } + } + if (retDest != null && tgtmtd.getReturnType() instanceof ReferenceType) { + this.addLoadEdge(receiver, retDest); + } + LocalVarNode stmtThrowNode = srcnf.makeInvokeStmtThrowVarNode(s, method); + this.addLoadEdge(receiver, stmtThrowNode); + this.addStoreEdge(receiver, receiver); // do not move this out of loop + } + } + } + + // add field edges + prePAG + .getContextFields() + .forEach( + contextField -> { + AllocNode base = contextField.getBase(); + SparkField field = contextField.getField(); + if (!prePAG.simpleInvLookup(contextField).isEmpty()) { + this.addHloadEdge(base, field); + } + if (!prePAG.simpleLookup(contextField).isEmpty()) { + this.addHstoreEdge(field, base); + } + }); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/mahjong/HeapAbstraction.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/mahjong/HeapAbstraction.java new file mode 100644 index 00000000000..402e6ec22ba --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/mahjong/HeapAbstraction.java @@ -0,0 +1,136 @@ +package qilin.pta.toolkits.mahjong; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +import qilin.core.pag.AllocNode; +import qilin.core.pag.Node; +import qilin.pta.toolkits.common.FieldPointstoGraph; +import qilin.pta.toolkits.mahjong.automata.DFA; +import qilin.pta.toolkits.mahjong.automata.DFAEquivalenceChecker; +import qilin.pta.toolkits.mahjong.automata.DFAFactory; +import qilin.pta.toolkits.mahjong.automata.DFAState; +import qilin.util.UnionFindSet; +import sootup.core.types.Type; + +/** + * @author Tian Tan + * @author Yue Li refactered by Dongjie He. + */ +public class HeapAbstraction { + + private final FieldPointstoGraph fpg; + private final DFAFactory dfaFactory; + private final DFAEquivalenceChecker dfaEqChecker; + + /** This map would be manipulated by multiple threads thus it should be concurrent hashmap. */ + private Map canMerged; + + public HeapAbstraction(FieldPointstoGraph fpg) { + this.fpg = fpg; + this.dfaFactory = new DFAFactory(fpg); + this.dfaEqChecker = new DFAEquivalenceChecker(); + } + + public Map computeMergedObjectMap() { + UnionFindSet uf = modelHeap(); + return convertToMap(uf.getDisjointSets()); + } + + /** Modeling the heap by checking the equivalence of automata. */ + private UnionFindSet modelHeap() { + canMerged = new ConcurrentHashMap<>(); + Set allObjs = fpg.getAllObjs(); + UnionFindSet uf = new UnionFindSet<>(allObjs); + // group the objects by their types + Map> groupedObjs = + allObjs.stream().collect(Collectors.groupingBy(Node::getType, Collectors.toSet())); + groupedObjs + .entrySet() + .parallelStream() + .forEach( + entry -> { + DFAMap dfaMap = new DFAMap(); + Set objs = + entry.getValue().stream() + .filter(o -> canBeMerged(o, dfaMap)) + .collect(Collectors.toSet()); + for (AllocNode o1 : objs) { + for (AllocNode o2 : objs) { + if (o1.getNumber() <= o2.getNumber() && !uf.isConnected(o1, o2)) { + if (canBeMerged(o1, o2, dfaMap)) { + uf.union(o1, o2); + } + } + } + } + }); + return uf; + } + + /** + * @param o1 + * @param o2 + * @return whether o1 and o2 can be merged. + */ + private boolean canBeMerged(AllocNode o1, AllocNode o2, DFAMap dfaMap) { + if (o1 == o2) { + return true; + } + DFA dfa1 = dfaMap.retrieveDFA(o1); + DFA dfa2 = dfaMap.retrieveDFA(o2); + return dfaEqChecker.isEquivalent(dfa1, dfa2); + } + + /** + * @param o + * @return whether o can be merged with other objects. + */ + private boolean canBeMerged(AllocNode o, DFAMap dfaMap) { + if (!canMerged.containsKey(o)) { + boolean result = true; + // Check whether the types of objects pointed (directly/indirectly) + // by o are unique. + DFA dfa = dfaMap.retrieveDFA(o); + for (DFAState s : dfa.getStates()) { + if (dfa.outputOf(s).size() > 1) { + // Types pointed (directly/indirectly) by o are not unique. + result = false; + break; + } + } + canMerged.put(o, result); + } + return canMerged.get(o); + } + + private static Map convertToMap(Collection> objModel) { + Map map = new HashMap<>(); + objModel.forEach( + objs -> { + AllocNode rep = selectRepresentative(objs); + objs.forEach(obj -> map.put(obj, rep)); + }); + return map; + } + + private static AllocNode selectRepresentative(Set objs) { + return objs.stream().findFirst().get(); + } + + /** + * During equivalence check, each thread holds a DFAMap which contains the DFA of the objects of + * the type. After comparison, the DFAMap and its containing DFA will be released to save memory + * space. + */ + private class DFAMap { + private final Map dfaMap = new HashMap<>(); + + private DFA retrieveDFA(AllocNode o) { + return dfaMap.computeIfAbsent(o, k -> dfaFactory.getDFA(o)); + } + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/mahjong/Mahjong.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/mahjong/Mahjong.java new file mode 100644 index 00000000000..e86901e57e4 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/mahjong/Mahjong.java @@ -0,0 +1,74 @@ +package qilin.pta.toolkits.mahjong; + +import java.util.Collection; +import java.util.Map; +import qilin.core.PTA; +import qilin.core.pag.AllocNode; +import qilin.pta.toolkits.common.FieldPointstoGraph; +import qilin.util.Stopwatch; + +/** + * @author Tian Tan + * @author Yue Li + */ +public class Mahjong { + public static void run(PTA pta, Map heapModelMap) { + FieldPointstoGraph fpg = buildFPG(pta); + System.out.print("Creating heap abstraction ... "); + Stopwatch mahjongTimer = Stopwatch.newAndStart("Mahjong"); + HeapAbstraction heapAbs = new HeapAbstraction(fpg); + Map mom = heapAbs.computeMergedObjectMap(); + mahjongTimer.stop(); + System.out.println(mahjongTimer); + outputStatistics(fpg, mom); + + System.out.print("Writing Mahjong heap abstraction ...\n"); + writeMergedObjectMap(mom, heapModelMap); + } + + public static FieldPointstoGraph buildFPG(PTA pta) { + System.out.print("Building FPG (Field Points-to Graph) ... "); + Stopwatch fpgTimer = Stopwatch.newAndStart("FPG Construction"); + FieldPointstoGraph fpg = new FieldPointstoGraph(pta); + fpgTimer.stop(); + System.out.println(fpgTimer); + return fpg; + } + + public static void outputStatistics(FieldPointstoGraph fpg, Map mom) { + int nObj = (int) mom.keySet().stream().distinct().count(); + int nObjMahjong = (int) mom.values().stream().distinct().count(); + System.out.println("-----------------------------------------------------------"); + System.out.printf("%d objects in the allocation-site heap abstraction.\n", nObj); + System.out.printf("%d objects in the Mahjong heap abstraction.\n", nObjMahjong); + System.out.println("-----------------------------------------------------------"); + + int nType = (int) fpg.getAllObjs().stream().map(AllocNode::getType).distinct().count(); + int nField = + (int) + fpg.getAllObjs().stream() + .map(fpg::outFieldsOf) + .flatMap(Collection::stream) + .distinct() + .count(); + int nObj2 = fpg.getAllObjs().size(); + // int nObjMahjong = model.size(); + + System.out.println("-----------------------------------------------------------"); + System.out.println("In the FPG (allocation-site heap abstraction), there are:"); + System.out.printf("%10d types\n", nType); + System.out.printf("%10d fields\n", nField); + System.out.printf("%10d objects\n", nObj2); + System.out.println("In the Mahjong heap abstraction, there are:"); + System.out.printf("%10d objects\n", nObjMahjong); + System.out.println("-----------------------------------------------------------"); + } + + private static void writeMergedObjectMap( + Map mom, Map heapModelMap) { + mom.forEach( + (heap, mergedHeap) -> { + heapModelMap.put(heap.getNewExpr(), mergedHeap.getNewExpr()); + }); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/mahjong/automata/DFA.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/mahjong/automata/DFA.java new file mode 100644 index 00000000000..2e1076fd55d --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/mahjong/automata/DFA.java @@ -0,0 +1,67 @@ +package qilin.pta.toolkits.mahjong.automata; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Queue; +import java.util.Set; +import sootup.core.types.Type; + +/** + * @author Tian Tan + * @author Yue Li + *

refactered by Dongjie He. + */ +public class DFA { + + private Set states, allStates; + private final DFAState q0; + + static final DFAState deadState = new DFAState(); + + public DFA(DFAState q0) { + this.q0 = q0; + } + + /** @return Set of states. Does not contains dead state. */ + public Set getStates() { + if (states == null) { + computeStates(); + } + return states; + } + + /** @return Set of all states including dead state. */ + public Set getAllStates() { + if (allStates == null) { + computeStates(); + } + return allStates; + } + + private void computeStates() { + Queue queue = new LinkedList<>(); + queue.add(q0); + states = new HashSet<>(); + while (!queue.isEmpty()) { + DFAState s = queue.poll(); + if (!states.contains(s)) { + states.add(s); + queue.addAll(s.getNextMap().values()); + } + } + allStates = new HashSet<>(states); + allStates.add(deadState); + } + + public DFAState getStartState() { + return q0; + } + + public static DFAState getDeadState() { + return deadState; + } + + public Set outputOf(DFAState s) { + return s.getOutput(); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/mahjong/automata/DFAEquivalenceChecker.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/mahjong/automata/DFAEquivalenceChecker.java new file mode 100644 index 00000000000..bc7861327c4 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/mahjong/automata/DFAEquivalenceChecker.java @@ -0,0 +1,93 @@ +package qilin.pta.toolkits.mahjong.automata; + +import java.util.Collection; +import java.util.Set; +import java.util.Stack; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import qilin.core.pag.SparkField; +import qilin.util.Pair; +import qilin.util.UnionFindSet; +import sootup.core.types.Type; + +/** + * @author Tian Tan + * @author Yue Li + */ +public class DFAEquivalenceChecker { + + /** + * Check the equivalence of input automata by Hopcroft-Karp algorithm with minor modifications. + * + * @param dfa1 + * @param dfa2 + * @return whether dfa1 and dfa2 are equivalent + */ + public boolean isEquivalent(DFA dfa1, DFA dfa2) { + CombinedDFA dfa = new CombinedDFA(dfa1, dfa2); + Set combinedStates = dfa.getStates(); + UnionFindSet uf = new UnionFindSet<>(combinedStates); + Stack> stack = new Stack<>(); + + DFAState s1 = dfa1.getStartState(); + DFAState s2 = dfa2.getStartState(); + uf.union(s1, s2); + stack.push(new Pair<>(s1, s2)); + while (!stack.isEmpty()) { + Pair pair = stack.pop(); + DFAState q1 = pair.getFirst(); + DFAState q2 = pair.getSecond(); + Stream.concat(dfa.outEdgesOf(q1).stream(), dfa.outEdgesOf(q2).stream()) + .forEach( + field -> { + DFAState r1 = uf.find(q1.nextState(field)); + DFAState r2 = uf.find(q2.nextState(field)); + if (r1 != r2) { + uf.union(r1, r2); + stack.push(new Pair<>(r1, r2)); + } + }); + } + Collection> mergedStateSets = uf.getDisjointSets(); + return validate(dfa, mergedStateSets); + } + + /** + * @param dfa + * @param mergedStateSets + * @return true if every state set contains no different output (i.e., types) + */ + private boolean validate(CombinedDFA dfa, Collection> mergedStateSets) { + for (Set set : mergedStateSets) { + int minSize = set.stream().mapToInt(s -> dfa.outputOf(s).size()).min().getAsInt(); + long unionSize = set.stream().flatMap(s -> dfa.outputOf(s).stream()).distinct().count(); + if (unionSize > minSize) { + return false; + } + } + return true; + } + + private static class CombinedDFA { + + DFA dfa1, dfa2; + + private CombinedDFA(DFA dfa1, DFA dfa2) { + this.dfa1 = dfa1; + this.dfa2 = dfa2; + } + + private Set getStates() { + return Stream.concat(dfa1.getAllStates().stream(), dfa2.getAllStates().stream()) + .collect(Collectors.toSet()); + } + + private Set outEdgesOf(DFAState s) { + return s.outEdges(); + } + + private Set outputOf(DFAState s) { + return s.getOutput(); + } + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/mahjong/automata/DFAFactory.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/mahjong/automata/DFAFactory.java new file mode 100644 index 00000000000..e419a21c769 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/mahjong/automata/DFAFactory.java @@ -0,0 +1,98 @@ +package qilin.pta.toolkits.mahjong.automata; + +import java.util.*; +import java.util.stream.Collectors; +import qilin.core.pag.AllocNode; +import qilin.core.pag.SparkField; +import qilin.pta.toolkits.common.FieldPointstoGraph; +import sootup.core.types.Type; + +/** + * @author Tian Tan + * @author Yue Li + */ +public class DFAFactory { + + private final FieldPointstoGraph fpg; + + private Map, DFAState> stateMap; + private Set states, visited; + + public DFAFactory(FieldPointstoGraph fpg) { + this.fpg = fpg; + buildAllDFA(); + } + + public DFA getDFA(AllocNode obj) { + DFAState q0 = stateMap.get(Collections.singleton(obj)); + return new DFA(q0); + } + + private void buildAllDFA() { + stateMap = new HashMap<>(); + states = new HashSet<>(); + visited = new HashSet<>(); + fpg.getAllObjs().forEach(this::buildDFA); + } + + /** + * Perform subset construction algorithm to convert an NFA to a DFA. If a set of NFA states are + * merged to an existing DFA state, then reused the existing DFA state instead of creating an + * equivalent new one. + * + * @param obj the start state (object) of the DFA + */ + private void buildDFA(AllocNode obj) { + Set q0Set = Collections.singleton(obj); + if (!stateMap.containsKey(q0Set)) { + NFA nfa = new NFA(obj, fpg); + DFAState startState = getDFAState(q0Set, nfa); + Queue worklist = new LinkedList<>(); + states.add(startState); + worklist.add(startState); + while (!worklist.isEmpty()) { + DFAState s = worklist.poll(); + if (!visited.contains(s)) { + visited.add(s); + Set fields = fields(nfa, s.getObjects()); + fields.forEach( + f -> { + Set nextNFAStates = move(nfa, s.getObjects(), f); + DFAState nextState = getDFAState(nextNFAStates, nfa); + if (!states.contains(nextState)) { + states.add(nextState); + worklist.add(nextState); + } + addTransition(s, f, nextState); + }); + } + } + } + } + + private DFAState getDFAState(Set objs, NFA nfa) { + if (!stateMap.containsKey(objs)) { + Set output = objs.stream().map(nfa::outputOf).collect(Collectors.toSet()); + stateMap.put(objs, new DFAState(objs, output)); + } + return stateMap.get(objs); + } + + private Set move(NFA nfa, Set objs, SparkField f) { + return objs.stream() + .map(obj -> nfa.nextStates(obj, f)) + .flatMap(Collection::stream) + .collect(Collectors.toSet()); + } + + private Set fields(NFA nfa, Set objs) { + return objs.stream() + .map(nfa::outEdgesOf) + .flatMap(Collection::stream) + .collect(Collectors.toSet()); + } + + private void addTransition(DFAState s, SparkField f, DFAState nextState) { + s.addTransition(f, nextState); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/mahjong/automata/DFAState.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/mahjong/automata/DFAState.java new file mode 100644 index 00000000000..c4265028350 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/mahjong/automata/DFAState.java @@ -0,0 +1,88 @@ +package qilin.pta.toolkits.mahjong.automata; + +import java.util.*; +import java.util.stream.Collectors; +import qilin.core.pag.AllocNode; +import qilin.core.pag.SparkField; +import sootup.core.types.Type; + +/** + * @author Tian Tan + * @author Yue Li + */ +public class DFAState { + + private final Set objs; + private final Set output; + private final Map nextMap; + + private boolean hasHashCode = false; + private int hashCode; + + public DFAState() { + this.objs = Collections.emptySet(); + this.output = Collections.emptySet(); + this.nextMap = new HashMap<>(); + } + + public DFAState(Set objs, Set output) { + this.objs = objs; + this.output = output; + this.nextMap = new HashMap<>(); + } + + public Set getObjects() { + return objs; + } + + public Set getOutput() { + return output; + } + + void addTransition(SparkField f, DFAState nextState) { + nextMap.put(f, nextState); + } + + Map getNextMap() { + return nextMap; + } + + DFAState nextState(SparkField f) { + return nextMap.getOrDefault(f, DFA.getDeadState()); + } + + Set outEdges() { + return nextMap.keySet(); + } + + /** Cache hash code. */ + @Override + public int hashCode() { + if (!hasHashCode) { + hashCode = objs.hashCode(); + hasHashCode = true; + } + return hashCode; + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!(other instanceof DFAState)) { + return false; + } + DFAState anoDFAState = (DFAState) other; + return getObjects().equals(anoDFAState.getObjects()); + } + + @Override + public String toString() { + return getObjects().stream() + .map(obj -> obj == null ? "null" : String.valueOf(obj.getNumber())) + .sorted() + .collect(Collectors.toCollection(LinkedHashSet::new)) + .toString(); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/mahjong/automata/NFA.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/mahjong/automata/NFA.java new file mode 100644 index 00000000000..ce95a8adf7c --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/mahjong/automata/NFA.java @@ -0,0 +1,88 @@ +package qilin.pta.toolkits.mahjong.automata; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.Stack; +import qilin.core.pag.AllocNode; +import qilin.core.pag.SparkField; +import qilin.pta.toolkits.common.FieldPointstoGraph; +import sootup.core.types.Type; + +/** + * @author Tian Tan + * @author Yue Li + */ +public class NFA { + + private static final AllocNode deadState = null; + + private final AllocNode q0; + private final FieldPointstoGraph fpg; + + public NFA(AllocNode q0, FieldPointstoGraph fpg) { + this.q0 = q0; + this.fpg = fpg; + } + + /** + * This method on-the-fly computes set of states. + * + * @return Set of states. Does not contains dead state. + */ + public Set getStates() { + Set states = new HashSet<>(); + Stack stack = new Stack<>(); + stack.push(q0); + while (!stack.isEmpty()) { + AllocNode s = stack.pop(); + if (!states.contains(s)) { + states.add(s); + outEdgesOf(s) + .forEach( + field -> { + nextStates(s, field).stream() + .filter(obj -> !states.contains(obj)) + .forEach(stack::push); + }); + } + } + return states; + } + + public AllocNode getStartState() { + return q0; + } + + public AllocNode getDeadState() { + return deadState; + } + + public Set nextStates(AllocNode obj, SparkField f) { + if (isDeadState(obj) || !fpg.hasFieldPointer(obj, f)) { + return Collections.singleton(deadState); + } else { + return fpg.pointsTo(obj, f); + } + } + + public boolean isDeadState(AllocNode obj) { + return obj == deadState; + } + + public Set outEdgesOf(AllocNode obj) { + if (isDeadState(obj)) { + return Collections.emptySet(); + } else { + return fpg.outFieldsOf(obj); + } + } + + public Type outputOf(AllocNode obj) { + if (isDeadState(obj)) { + return null; + } else { + return obj.getType(); + } + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/mahjong/gpl-3.0.txt b/sootup.qilin/src/main/java/qilin/pta/toolkits/mahjong/gpl-3.0.txt new file mode 100644 index 00000000000..f288702d2fa --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/mahjong/gpl-3.0.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/selectx/BNode.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/selectx/BNode.java new file mode 100644 index 00000000000..2cf66d80bbb --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/selectx/BNode.java @@ -0,0 +1,58 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.selectx; + +import java.util.stream.Stream; +import qilin.core.pag.Node; + +public abstract class BNode { + public Node sparkNode; + + /** + * visited is not overrided by para: this represents value flows in *any* contexts, this can avoid + * unmatched exit without missing exit from inner alloc or global + */ + private boolean visited = false; + + BNode(Node origin) { + this.sparkNode = origin; + } + + public void reset() { + visited = false; + } + + public boolean isVisited() { + return this.visited; + } + + public boolean setVisited() { + if (visited) return false; + return visited = true; + } + + public abstract boolean addOutEdge(BNode to); + + public abstract Stream forwardTargets(); + + @Override + public String toString() { + return this.getClass().toString() + " : " + sparkNode.toString(); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/selectx/G.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/selectx/G.java new file mode 100644 index 00000000000..f54be2fc501 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/selectx/G.java @@ -0,0 +1,58 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.selectx; + +import java.util.*; +import java.util.stream.Stream; +import qilin.core.pag.GlobalVarNode; + +public class G extends BNode { + public static Map g2GN = new HashMap<>(); + public static Map g2GP = new HashMap<>(); + + public static G v(GlobalVarNode origin, boolean positive) { + if (positive) { + return g2GP.computeIfAbsent(origin, k -> new G(origin, true)); + } else { + return g2GN.computeIfAbsent(origin, k -> new G(origin, false)); + } + } + + private final Set outLs = new HashSet<>(); + private final boolean positive; + + G(GlobalVarNode origin, boolean positive) { + super(origin); + this.positive = positive; + } + + public boolean addOutEdge(L toE) { + return outLs.add(toE); + } + + @Override + public boolean addOutEdge(BNode toE) { + return addOutEdge((L) toE); + } + + @Override + public Stream forwardTargets() { + return outLs.stream(); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/selectx/I.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/selectx/I.java new file mode 100644 index 00000000000..a7b278c5447 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/selectx/I.java @@ -0,0 +1,60 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.selectx; + +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Stream; +import qilin.core.pag.Node; + +/** local nodes */ +public abstract class I extends BNode { + private final Set outIs = new HashSet<>(); + protected Set paras = new HashSet<>(); + + I(Node origin) { + super(origin); + } + + public void clearParas() { + this.paras.clear(); + } + + boolean update(I another) { + return this.paras.addAll(another.paras); + } + + public boolean addOutEdge(I to) { + return outIs.add(to); + } + + @Override + public boolean addOutEdge(BNode toE) { + return addOutEdge((I) toE); + } + + @Override + public Stream forwardTargets() { + return outIs.stream(); + } + + public Set getOutTargets() { + return this.outIs; + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/selectx/L.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/selectx/L.java new file mode 100644 index 00000000000..e5fa38d4fa7 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/selectx/L.java @@ -0,0 +1,102 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.selectx; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import qilin.core.pag.LocalVarNode; +import qilin.util.Util; + +/** locals */ +public class L extends I { + public static Map l2LN = new HashMap<>(); + public static Map l2LP = new HashMap<>(); + + public static L v(LocalVarNode origin, boolean positive) { + if (positive) { + return l2LP.computeIfAbsent(origin, k -> new L(origin, positive)); + } else { + return l2LN.computeIfAbsent(origin, k -> new L(origin, positive)); + } + } + + private final Set outGs = new HashSet<>(); + private final boolean positive; + private final Map> outEntryEdges = new HashMap<>(); + private final Map> outExitEdges = new HashMap<>(); + private final Map> inEntryEdges = new HashMap<>(); + + L(LocalVarNode origin, boolean positive) { + super(origin); + this.positive = positive; + } + + public boolean addOutEdge(G to) { + return outGs.add(to); + } + + public Stream getOutGs() { + return outGs.stream(); + } + + @Override + public boolean addOutEdge(BNode toE) { + if (toE instanceof G) return addOutEdge((G) toE); + return super.addOutEdge(toE); + } + + public boolean addOutEntryEdge(int i, L toE) { + return Util.addToMap(outEntryEdges, i, toE); + } + + public boolean addOutExitEdge(int i, L toE) { + return Util.addToMap(outExitEdges, i, toE); + } + + public Set getOutEntryEdges() { + return outEntryEdges.values().stream().flatMap(Collection::stream).collect(Collectors.toSet()); + } + + public Set>> getOutExitEdges() { + return outExitEdges.entrySet(); + } + + public boolean addInEntryEdge(int i, L fromE) { + return Util.addToMap(inEntryEdges, i, fromE); + } + + public Set getInEntryEdges(int i) { + return inEntryEdges.getOrDefault(i, Collections.emptySet()); + } + + @Override + public Stream forwardTargets() { + Stream exits = outExitEdges.values().stream().flatMap(Collection::stream); + Stream outs = Stream.concat(exits, outGs.stream()); + return Stream.concat(outs, super.forwardTargets()); + } + + /* + * inverse operation: inv(l-) = l+ and inv(l+) = l-. + * */ + public L inv() { + return L.v((LocalVarNode) sparkNode, !positive); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/selectx/O.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/selectx/O.java new file mode 100644 index 00000000000..38fd570c397 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/selectx/O.java @@ -0,0 +1,35 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.selectx; + +import java.util.HashMap; +import java.util.Map; +import qilin.core.pag.AllocNode; + +public class O extends I { + public static Map o2O = new HashMap<>(); + + public static O v(AllocNode origin) { + return o2O.computeIfAbsent(origin, k -> new O(origin)); + } + + public O(AllocNode origin) { + super(origin); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/selectx/Selectx.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/selectx/Selectx.java new file mode 100644 index 00000000000..4ca2eb37ff2 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/selectx/Selectx.java @@ -0,0 +1,356 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.selectx; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import qilin.core.PTA; +import qilin.core.PointsToAnalysis; +import qilin.core.builder.MethodNodeFactory; +import qilin.core.builder.callgraph.Edge; +import qilin.core.pag.*; +import qilin.util.PTAUtils; +import qilin.util.queue.QueueReader; +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.basic.Value; +import sootup.core.jimple.common.constant.NullConstant; +import sootup.core.jimple.common.expr.AbstractInstanceInvokeExpr; +import sootup.core.jimple.common.expr.AbstractInvokeExpr; +import sootup.core.jimple.common.stmt.JAssignStmt; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.SootMethod; +import sootup.core.types.ReferenceType; + +public class Selectx { + private final PTA prePTA; + private final PAG prePAG; + private final Set sparkFields = new HashSet<>(); + + public Selectx(PTA pta) { + this.prePTA = pta; + this.prePAG = pta.getPag(); + buildGraph(); + } + + public void addNewEdge(AllocNode from, LocalVarNode to) { + O fromE = O.v(from); + L toE = L.v(to, true); + fromE.addOutEdge(toE); + L toEI = L.v(to, false); + toEI.addOutEdge(fromE); + } + + public void addAssignEdge(LocalVarNode from, LocalVarNode to) { + L fromE = L.v(from, true), toE = L.v(to, true); + fromE.addOutEdge(toE); + L fromEI = L.v(from, false), toEI = L.v(to, false); + toEI.addOutEdge(fromEI); + } + + public void addEntryEdge(LocalVarNode from, LocalVarNode to, CallSite callSite) { + int i = getCallSiteNumber(callSite); + + L fromE = L.v(from, true), toE = L.v(to, true); + if (fromE.addOutEntryEdge(i, toE)) { + toE.addInEntryEdge(i, fromE); + L fromEI = L.v(from, false), toEI = L.v(to, false); + toEI.addOutExitEdge(i, fromEI); + } + } + + public void addExitEdge(LocalVarNode from, LocalVarNode to, CallSite callSite) { + int i = getCallSiteNumber(callSite); + + L fromE = L.v(from, true), toE = L.v(to, true); + if (fromE.addOutExitEdge(i, toE)) { + L fromEI = L.v(from, false), toEI = L.v(to, false); + toEI.addOutEntryEdge(i, fromEI); + fromEI.addInEntryEdge(i, toEI); + } + } + + public void addStoreEdge(LocalVarNode from, LocalVarNode base) { + L fromE = L.v(from, true), baseE = L.v(base, true); + L fromEI = L.v(from, false), baseEI = L.v(base, false); + fromE.addOutEdge(baseEI); + baseE.addOutEdge(fromEI); + } + + public void addStaticStoreEdge(LocalVarNode from, GlobalVarNode to) { + L fromE = L.v(from, true); + G toE = G.v(to, true); + fromE.addOutEdge(toE); + L fromEI = L.v(from, false); + G toEI = G.v(to, false); + toEI.addOutEdge(fromEI); + } + + public void addStaticLoadEdge(GlobalVarNode from, LocalVarNode to) { + G fromE = G.v(from, true); + L toE = L.v(to, true); + fromE.addOutEdge(toE); + G fromEI = G.v(from, false); + L toEI = L.v(to, false); + toEI.addOutEdge(fromEI); + } + + private void propagate(Set workList, Set paraWorkList) { + while (!workList.isEmpty() || !paraWorkList.isEmpty()) { + while (!workList.isEmpty()) { + BNode node = workList.iterator().next(); + workList.remove(node); + node.forwardTargets().filter(BNode::setVisited).forEach(workList::add); + if (node instanceof L) { + L l = (L) node; + l.getOutEntryEdges().stream() + .filter(tgt -> tgt.paras.add(tgt)) + .forEach(paraWorkList::add); + } + } + while (!paraWorkList.isEmpty()) { + I node = paraWorkList.iterator().next(); + paraWorkList.remove(node); + // para propagation. + node.getOutTargets().stream().filter(i -> i.update(node)).forEach(paraWorkList::add); + if (node instanceof L) { + L l = (L) node; + l.getOutGs().filter(BNode::setVisited).forEach(workList::add); + l.getOutEntryEdges().stream() + .filter(tgt -> tgt.paras.add(tgt)) + .forEach(paraWorkList::add); + for (Map.Entry> entry : l.getOutExitEdges()) { + Integer i = entry.getKey(); + Set tgts = entry.getValue(); + l.paras.stream() + .flatMap(para -> para.getInEntryEdges(i).stream()) + .forEach( + arg -> { + tgts.forEach( + tgt -> { + if (arg.addOutEdge(tgt)) { // add match edge + if (arg.isVisited() && tgt.setVisited()) { + workList.add(tgt); + } + if (tgt.update(arg)) { + paraWorkList.add(tgt); + } + } + }); + }); + } + } + } // para while + } // outer while + } + + private void resetNodes() { + // reset nodes' visited state + G.g2GN.values().forEach(BNode::reset); + G.g2GP.values().forEach(BNode::reset); + L.l2LN.values().forEach(BNode::reset); + L.l2LP.values().forEach(BNode::reset); + O.o2O.values().forEach(BNode::reset); + // clear paras + L.l2LN.values().forEach(I::clearParas); + L.l2LP.values().forEach(I::clearParas); + O.o2O.values().forEach(I::clearParas); + } + + public Map process() { + System.out.print("cs2 propogating ..."); + long time = System.currentTimeMillis(); + + Set workList = new HashSet<>(); + Set paraWorkList = new HashSet<>(); + + // forward processing... + O.o2O + .values() + .forEach( + o -> { + o.setVisited(); + workList.add(o); + }); + propagate(workList, paraWorkList); + + // record and reset... + Set entryO = + O.o2O.values().stream().filter(o -> !o.paras.isEmpty()).collect(Collectors.toSet()); + Set entryL = + Stream.concat(L.l2LP.values().stream(), L.l2LN.values().stream()) + .filter(l -> !l.paras.isEmpty()) + .collect(Collectors.toSet()); + resetNodes(); + + // backward processing... + L.l2LN + .values() + .forEach( + ln -> { + ln.setVisited(); + workList.add(ln); + }); + propagate(workList, paraWorkList); + + System.out.println((System.currentTimeMillis() - time) / 1000 + "s"); + + Map ret = new HashMap<>(); + entryO.forEach( + o -> { + if (!o.paras.isEmpty()) { + ret.put(o.sparkNode, 1); + } else { + ret.put(o.sparkNode, 0); + } + }); + entryL.forEach( + l -> { + if (!l.inv().paras.isEmpty()) { + ret.put(l.sparkNode, 1); + } else { + ret.put(l.sparkNode, 0); + } + }); + this.sparkFields.forEach( + f -> { + ret.put(f, 1); + }); + return ret; + } + + Map call2Number = new HashMap<>(); + int totalCallsites = 0; + + int getCallSiteNumber(CallSite callsite) { + Integer oldNumber = call2Number.get(callsite); + if (oldNumber != null) { + return oldNumber; + } + totalCallsites++; + call2Number.put(callsite, totalCallsites); + return totalCallsites; + } + + private void buildGraph() { + for (SootMethod method : prePTA.getNakedReachableMethods()) { + if (!PTAUtils.hasBody(method)) { + continue; + } + MethodPAG srcmpag = prePAG.getMethodPAG(method); + QueueReader reader = srcmpag.getInternalReader().clone(); + while (reader.hasNext()) { + Node from = reader.next(), to = reader.next(); + if (from instanceof LocalVarNode) { + if (to instanceof LocalVarNode) { + this.addAssignEdge((LocalVarNode) from, (LocalVarNode) to); + } else if (to instanceof FieldRefNode) { + FieldRefNode fr = (FieldRefNode) to; + this.addStoreEdge((LocalVarNode) from, (LocalVarNode) fr.getBase()); + this.sparkFields.add(fr.getField()); + } else { // local-global + assert to instanceof GlobalVarNode; + this.addStaticStoreEdge((LocalVarNode) from, (GlobalVarNode) to); + } + } else if (from instanceof AllocNode) { + if (to instanceof LocalVarNode) { + this.addNewEdge((AllocNode) from, (LocalVarNode) to); + } // GlobalVarNode + } else if (from instanceof FieldRefNode) { + FieldRefNode fr = (FieldRefNode) from; + // load edge is treated as assign. + this.addAssignEdge((LocalVarNode) fr.getBase(), (LocalVarNode) to); + this.sparkFields.add(fr.getField()); + } else { + assert (from instanceof GlobalVarNode); + this.addStaticLoadEdge((GlobalVarNode) from, (LocalVarNode) to); + } + } + + // add exception edges that added dynamically during the pre-analysis. + srcmpag + .getExceptionEdges() + .forEach( + (k, vs) -> { + for (Node v : vs) { + this.addAssignEdge((LocalVarNode) k, (LocalVarNode) v); + } + }); + + // add invoke edges + MethodNodeFactory srcnf = srcmpag.nodeFactory(); + for (final Stmt s : srcmpag.getInvokeStmts()) { + CallSite callSite = new CallSite(s); + AbstractInvokeExpr ie = s.getInvokeExpr(); + int numArgs = ie.getArgCount(); + Value[] args = new Value[numArgs]; + for (int i = 0; i < numArgs; i++) { + Value arg = ie.getArg(i); + if (!(arg.getType() instanceof ReferenceType) || arg instanceof NullConstant) { + continue; + } + args[i] = arg; + } + LocalVarNode retDest = null; + if (s instanceof JAssignStmt) { + Value dest = ((JAssignStmt) s).getLeftOp(); + if (dest.getType() instanceof ReferenceType) { + retDest = prePAG.findLocalVarNode(method, dest, dest.getType()); + } + } + LocalVarNode receiver = null; + if (ie instanceof AbstractInstanceInvokeExpr) { + AbstractInstanceInvokeExpr iie = (AbstractInstanceInvokeExpr) ie; + Local base = iie.getBase(); + receiver = prePAG.findLocalVarNode(method, base, base.getType()); + } + for (Iterator it = prePTA.getCallGraph().edgesOutOf(s); it.hasNext(); ) { + Edge e = it.next(); + SootMethod tgtmtd = e.tgt(); + MethodPAG tgtmpag = prePAG.getMethodPAG(tgtmtd); + MethodNodeFactory tgtnf = tgtmpag.nodeFactory(); + for (int i = 0; i < numArgs; i++) { + if (args[i] == null || !(tgtmtd.getParameterType(i) instanceof ReferenceType)) { + continue; + } + LocalVarNode parm = (LocalVarNode) tgtnf.caseParm(i); + this.addEntryEdge((LocalVarNode) srcnf.getNode(args[i]), parm, callSite); + } + if (retDest != null && tgtmtd.getReturnType() instanceof ReferenceType) { + LocalVarNode ret = (LocalVarNode) tgtnf.caseRet(); + this.addExitEdge(ret, retDest, callSite); + } + LocalVarNode stmtThrowNode = srcnf.makeInvokeStmtThrowVarNode(s, method); + LocalVarNode throwFinal = + prePAG.findLocalVarNode( + method, + new Parm(tgtmtd, PointsToAnalysis.THROW_NODE), + PTAUtils.getClassType("java.lang.Throwable")); + if (throwFinal != null) { + this.addExitEdge(throwFinal, stmtThrowNode, callSite); + } + if (receiver != null) { + LocalVarNode thisRef = (LocalVarNode) tgtnf.caseThis(); + this.addEntryEdge(receiver, thisRef, callSite); + } + } + } + } // reachable methods. + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/turner/AbstractMVFG.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/turner/AbstractMVFG.java new file mode 100644 index 00000000000..1612242b541 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/turner/AbstractMVFG.java @@ -0,0 +1,304 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.turner; + +import java.util.*; +import qilin.core.PTA; +import qilin.core.PointsToAnalysis; +import qilin.core.builder.MethodNodeFactory; +import qilin.core.builder.callgraph.Edge; +import qilin.core.builder.callgraph.OnFlyCallGraph; +import qilin.core.pag.*; +import qilin.util.PTAUtils; +import qilin.util.Pair; +import qilin.util.queue.QueueReader; +import qilin.util.queue.UniqueQueue; +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.basic.Value; +import sootup.core.jimple.common.constant.NullConstant; +import sootup.core.jimple.common.expr.AbstractInstanceInvokeExpr; +import sootup.core.jimple.common.expr.AbstractInvokeExpr; +import sootup.core.jimple.common.stmt.JAssignStmt; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.SootMethod; +import sootup.core.types.ReferenceType; + +public abstract class AbstractMVFG { + public static Map method2VFG = new HashMap<>(); + protected final PTA prePTA; + protected final OCG hg; + protected final SootMethod method; + protected final Set sparkNodes = new HashSet<>(); + protected final Set csNodes = new HashSet<>(); + protected final Map> outEdges = new HashMap<>(); + protected final Map> inEdges = new HashMap<>(); + + protected int total_edge_count = 0; + + public static AbstractMVFG findMethodVFG(SootMethod method) { + return method2VFG.getOrDefault(method, null); + } + + public AbstractMVFG(PTA prePTA, OCG hg, SootMethod method) { + this.prePTA = prePTA; + this.hg = hg; + this.method = method; + } + + public Collection getAllNodes() { + return sparkNodes; + } + + public int getTotalNodeCount() { + return sparkNodes.size(); + } + + public int getTotalEdgeCount() { + return total_edge_count; + } + + public Collection getCSNodes() { + return csNodes; + } + + protected void addNormalEdge(TranEdge edge) { + sparkNodes.add(edge.getSource()); + sparkNodes.add(edge.getTarget()); + total_edge_count++; + outEdges.computeIfAbsent(edge.getSource(), k -> new HashSet<>()).add(edge); + inEdges.computeIfAbsent(edge.getTarget(), k -> new HashSet<>()).add(edge); + } + + protected void addNewEdge(AllocNode from, LocalVarNode to) { + TranEdge newEdge = new TranEdge(from, to, DFA.TranCond.NEW); + addNormalEdge(newEdge); + TranEdge newInvEdge = new TranEdge(to, from, DFA.TranCond.INEW); + addNormalEdge(newInvEdge); + } + + protected void addCSLikelyEdge(AllocNode heap) { + TranEdge csLikelyEdge = new TranEdge(heap, heap, DFA.TranCond.CSLIKELY); + addNormalEdge(csLikelyEdge); + } + + protected void addAssignEdge(LocalVarNode from, LocalVarNode to) { + TranEdge assignEdge = new TranEdge(from, to, DFA.TranCond.ASSIGN); + addNormalEdge(assignEdge); + TranEdge assignInvEdge = new TranEdge(to, from, DFA.TranCond.IASSIGN); + addNormalEdge(assignInvEdge); + } + + protected void addStoreEdge(LocalVarNode from, LocalVarNode base) { + TranEdge storeEdge = new TranEdge(from, base, DFA.TranCond.STORE); + addNormalEdge(storeEdge); + TranEdge storeInvEdge = new TranEdge(base, from, DFA.TranCond.ISTORE); + addNormalEdge(storeInvEdge); + } + + protected void addLoadEdge(LocalVarNode base, LocalVarNode to) { + TranEdge loadEdge = new TranEdge(base, to, DFA.TranCond.LOAD); + addNormalEdge(loadEdge); + TranEdge loadInvEdge = new TranEdge(to, base, DFA.TranCond.ILOAD); + addNormalEdge(loadInvEdge); + } + + protected void buildVFG() { + OnFlyCallGraph callGraph = prePTA.getCallGraph(); + PAG pag = prePTA.getPag(); + MethodPAG srcmpag = pag.getMethodPAG(method); + MethodNodeFactory srcnf = srcmpag.nodeFactory(); + LocalVarNode thisRef = (LocalVarNode) srcnf.caseThis(); + QueueReader reader = srcmpag.getInternalReader().clone(); + while (reader.hasNext()) { + Node from = reader.next(), to = reader.next(); + if (from instanceof LocalVarNode) { + if (to instanceof LocalVarNode) this.addAssignEdge((LocalVarNode) from, (LocalVarNode) to); + else if (to instanceof FieldRefNode) { + FieldRefNode fr = (FieldRefNode) to; + this.addStoreEdge((LocalVarNode) from, (LocalVarNode) fr.getBase()); + } // local-global + + } else if (from instanceof AllocNode) { + if (to instanceof LocalVarNode) { + this.addNewEdge((AllocNode) from, (LocalVarNode) to); + if (hg.isCSLikely((AllocNode) from)) { + this.addCSLikelyEdge((AllocNode) from); + } + } // GlobalVarNode + } else if (from instanceof FieldRefNode) { + FieldRefNode fr = (FieldRefNode) from; + this.addLoadEdge((LocalVarNode) fr.getBase(), (LocalVarNode) to); + } // global-local + } + // add exception edges that added dynamically during the pre-analysis. + srcmpag + .getExceptionEdges() + .forEach( + (k, vs) -> { + for (Node v : vs) { + this.addAssignEdge((LocalVarNode) k, (LocalVarNode) v); + } + }); + + // add invoke edges + for (final Stmt s : srcmpag.getInvokeStmts()) { + AbstractInvokeExpr ie = s.getInvokeExpr(); + int numArgs = ie.getArgCount(); + Value[] args = new Value[numArgs]; + for (int i = 0; i < numArgs; i++) { + Value arg = ie.getArg(i); + if (!(arg.getType() instanceof ReferenceType) || arg instanceof NullConstant) continue; + args[i] = arg; + } + LocalVarNode retDest = null; + if (s instanceof JAssignStmt) { + Value dest = ((JAssignStmt) s).getLeftOp(); + if (dest.getType() instanceof ReferenceType) { + retDest = pag.findLocalVarNode(method, dest, dest.getType()); + } + } + LocalVarNode receiver; + if (ie instanceof AbstractInstanceInvokeExpr) { + AbstractInstanceInvokeExpr iie = (AbstractInstanceInvokeExpr) ie; + Local base = iie.getBase(); + receiver = pag.findLocalVarNode(method, base, base.getType()); + } else { + // static call + receiver = thisRef; + } + + Set targets = new HashSet<>(); + for (Iterator it = callGraph.edgesOutOf(s); it.hasNext(); ) { + Edge e = it.next(); + SootMethod tgtmtd = e.tgt(); + targets.add(tgtmtd); + } + if (!targets.isEmpty()) { + for (int i = 0; i < numArgs; i++) { + if (args[i] == null) continue; + ValNode argNode = pag.findValNode(args[i], method); + if (argNode instanceof LocalVarNode && satisfyAddingStoreCondition(i, targets)) { + this.addStoreEdge((LocalVarNode) argNode, receiver); + } + } + if (retDest != null && retDest.getType() instanceof ReferenceType) { + if (statisfyAddingLoadCondition(targets)) { + this.addLoadEdge(receiver, retDest); + } + } + if (statisfyAddingLoadCondition(targets)) { + LocalVarNode stmtThrowNode = srcnf.makeInvokeStmtThrowVarNode(s, method); + this.addLoadEdge(receiver, stmtThrowNode); + } + if (satisfyAddingStoreCondition(PointsToAnalysis.THIS_NODE, targets)) { + this.addStoreEdge(receiver, receiver); + } + } + } + + // add param and return edges + addNormalEdge(new TranEdge(thisRef, thisRef, DFA.TranCond.PARAM)); + addNormalEdge(new TranEdge(thisRef, thisRef, DFA.TranCond.IPARAM)); + + int numParms = method.getParameterCount(); + for (int i = 0; i < numParms; i++) { + if (method.getParameterType(i) instanceof ReferenceType) { + LocalVarNode param = (LocalVarNode) srcnf.caseParm(i); + addNormalEdge(new TranEdge(param, param, DFA.TranCond.PARAM)); + addNormalEdge(new TranEdge(param, param, DFA.TranCond.IPARAM)); + } + } + if (method.getReturnType() instanceof ReferenceType) { + LocalVarNode mret = (LocalVarNode) srcnf.caseRet(); + addStoreEdge(mret, thisRef); + } + LocalVarNode mThrow = + pag.findLocalVarNode( + method, + new Parm(method, PointsToAnalysis.THROW_NODE), + PTAUtils.getClassType("java.lang.Exception")); + if (mThrow != null) { + addStoreEdge(mThrow, thisRef); + } + } + + protected abstract boolean statisfyAddingLoadCondition(Set targets); + + protected abstract boolean satisfyAddingStoreCondition(int paramIndex, Set targets); + + /* + * Algorithm1: x \in R(flow) \cap R(iflow). + * more efficient. + * */ + public void computeNodesInPrecisionLossPatterns() { + Queue> workList = new UniqueQueue<>(); + Map> state2nodes = new HashMap<>(); + MethodPAG srcmpag = prePTA.getPag().getMethodPAG(method); + MethodNodeFactory srcnf = srcmpag.nodeFactory(); + + // initialize worklist + int numParms = method.getParameterCount(); + LocalVarNode thisRef = (LocalVarNode) srcnf.caseThis(); + Set startState = state2nodes.computeIfAbsent(DFA.State.S, k -> new HashSet<>()); + startState.add(thisRef); + workList.add(new Pair<>(thisRef, DFA.State.S)); + + for (int i = 0; i < numParms; i++) { + if (method.getParameterType(i) instanceof ReferenceType) { + LocalVarNode param = (LocalVarNode) srcnf.caseParm(i); + startState.add(param); + workList.add(new Pair<>(param, DFA.State.S)); + } + } + if (method.getReturnType() instanceof ReferenceType) { + LocalVarNode mret = (LocalVarNode) srcnf.caseRet(); + startState.add(mret); + workList.add(new Pair<>(mret, DFA.State.S)); + } + LocalVarNode mThrow = (LocalVarNode) srcnf.caseMethodThrow(); + startState.add(mThrow); + workList.add(new Pair<>(mThrow, DFA.State.S)); + + // propagate + while (!workList.isEmpty()) { + Pair pair = workList.poll(); + Object currNode = pair.getFirst(); + DFA.State currState = pair.getSecond(); + for (TranEdge e : outEdges.getOrDefault(currNode, Collections.emptySet())) { + Object target = e.getTarget(); + DFA.TranCond tranCond = e.getTranCond(); + DFA.State nextState = DFA.nextState(currState, tranCond); + if (nextState != DFA.State.ERROR) { + Set stateNodes = state2nodes.computeIfAbsent(nextState, k -> new HashSet<>()); + if (stateNodes.add(target)) { + workList.add(new Pair<>(target, nextState)); + } + } + } + } + // collect cs nodes. + Set flowNodes = state2nodes.getOrDefault(DFA.State.FLOW, Collections.emptySet()); + Set iflowNodes = state2nodes.getOrDefault(DFA.State.IFLOW, Collections.emptySet()); + for (Object sparkNode : sparkNodes) { + if (flowNodes.contains(sparkNode) && iflowNodes.contains(sparkNode)) { + csNodes.add(sparkNode); + } + } + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/turner/DFA.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/turner/DFA.java new file mode 100644 index 00000000000..6681998ec59 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/turner/DFA.java @@ -0,0 +1,74 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.turner; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class DFA { + public enum State { + S, + FLOW, + IFLOW, + E, + ERROR; + } + + public enum TranCond { + PARAM, + IPARAM, + ASSIGN, + IASSIGN, + LOAD, + ILOAD, + STORE, + ISTORE, + NEW, + INEW, + CSLIKELY; + } + + private static final Map> transitionFunc = new HashMap<>(); + + static { + Map mS = transitionFunc.computeIfAbsent(State.S, k -> new HashMap<>()); + mS.put(TranCond.PARAM, State.FLOW); + + Map mFlow = transitionFunc.computeIfAbsent(State.FLOW, k -> new HashMap<>()); + mFlow.put(TranCond.ASSIGN, State.FLOW); + mFlow.put(TranCond.LOAD, State.FLOW); + mFlow.put(TranCond.STORE, State.IFLOW); + mFlow.put(TranCond.ISTORE, State.IFLOW); + mFlow.put(TranCond.NEW, State.FLOW); + + Map mIFlow = transitionFunc.computeIfAbsent(State.IFLOW, k -> new HashMap<>()); + mIFlow.put(TranCond.IASSIGN, State.IFLOW); + mIFlow.put(TranCond.ILOAD, State.IFLOW); + mIFlow.put(TranCond.IPARAM, State.E); + mIFlow.put(TranCond.INEW, State.IFLOW); + mIFlow.put(TranCond.CSLIKELY, State.FLOW); + } + + public static State nextState(State curr, TranCond tranCond) { + return transitionFunc + .getOrDefault(curr, Collections.emptyMap()) + .getOrDefault(tranCond, State.ERROR); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/turner/MethodLevelCallGraph.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/turner/MethodLevelCallGraph.java new file mode 100644 index 00000000000..a5dd0f36641 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/turner/MethodLevelCallGraph.java @@ -0,0 +1,71 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.turner; + +import java.util.Collection; +import qilin.core.builder.callgraph.OnFlyCallGraph; +import qilin.util.graph.DirectedGraph; +import qilin.util.graph.DirectedGraphImpl; +import sootup.core.model.SootMethod; + +public class MethodLevelCallGraph implements DirectedGraph { + private final OnFlyCallGraph callGraph; + private final DirectedGraphImpl mcg; + + public MethodLevelCallGraph(OnFlyCallGraph callGraph) { + this.callGraph = callGraph; + this.mcg = new DirectedGraphImpl<>(); + init(); + } + + private void init() { + callGraph + .iterator() + .forEachRemaining( + edge -> { + SootMethod src = edge.getSrc().method(); + SootMethod tgt = edge.getTgt().method(); + if (src != null && tgt != null) { + mcg.addEdge(src, tgt); + } else { + if (src != null) { + mcg.addNode(src); + } + if (tgt != null) { + mcg.addNode(tgt); + } + } + }); + } + + @Override + public Collection allNodes() { + return mcg.allNodes(); + } + + @Override + public Collection predsOf(SootMethod n) { + return mcg.predsOf(n); + } + + @Override + public Collection succsOf(final SootMethod n) { + return mcg.succsOf(n); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/turner/MethodVFG.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/turner/MethodVFG.java new file mode 100644 index 00000000000..b5c7816b310 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/turner/MethodVFG.java @@ -0,0 +1,44 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.turner; + +import java.util.Set; +import qilin.core.PTA; +import sootup.core.model.SootMethod; + +public class MethodVFG extends AbstractMVFG { + public static AbstractMVFG findOrCreateMethodVFG(PTA prePTA, SootMethod method, OCG hg) { + return method2VFG.computeIfAbsent(method, k -> new MethodVFG(prePTA, method, hg)); + } + + public MethodVFG(PTA prePTA, SootMethod method, OCG hg) { + super(prePTA, hg, method); + buildVFG(); + } + + @Override + protected boolean statisfyAddingLoadCondition(Set targets) { + return true; + } + + @Override + protected boolean satisfyAddingStoreCondition(int paramIndex, Set targets) { + return true; + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/turner/ModularMVFG.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/turner/ModularMVFG.java new file mode 100644 index 00000000000..a8628efa1fa --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/turner/ModularMVFG.java @@ -0,0 +1,94 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.turner; + +import java.util.Set; +import qilin.core.PTA; +import qilin.core.PointsToAnalysis; +import qilin.core.builder.MethodNodeFactory; +import qilin.core.pag.LocalVarNode; +import qilin.core.pag.MethodPAG; +import qilin.core.pag.Node; +import qilin.util.graph.MergedNode; +import sootup.core.model.SootMethod; +import sootup.core.types.ReferenceType; + +public class ModularMVFG extends AbstractMVFG { + public static AbstractMVFG findOrCreateMethodVFG( + PTA prePTA, SootMethod method, OCG hg, MergedNode sccNode) { + return method2VFG.computeIfAbsent(method, k -> new ModularMVFG(prePTA, method, hg, sccNode)); + } + + private final MergedNode sccNode; + + public ModularMVFG(PTA prePTA, SootMethod method, OCG hg, MergedNode sccNode) { + super(prePTA, hg, method); + this.sccNode = sccNode; + buildVFG(); + } + + @Override + protected boolean statisfyAddingLoadCondition(Set targets) { + for (SootMethod tgtmtd : targets) { + // the target method is in the same scc with current method. + if (!tgtmtd.isConcrete() || sccNode.getContent().contains(tgtmtd)) { + return true; + } + if (!(tgtmtd.getReturnType() instanceof ReferenceType)) { + continue; + } + MethodPAG tgtmpag = prePTA.getPag().getMethodPAG(tgtmtd); + AbstractMVFG tgtVfg = method2VFG.get(tgtmtd); + assert tgtVfg != null; + MethodNodeFactory tgtnf = tgtmpag.nodeFactory(); + Node ret = tgtnf.caseRet(); + if (tgtVfg.getCSNodes().contains(ret)) { + return true; + } + } + return false; + } + + protected boolean satisfyAddingStoreCondition(int paramIndex, Set targets) { + for (SootMethod tgtmtd : targets) { + // the target method is in the same scc with current method. + if (!tgtmtd.isConcrete() || sccNode.getContent().contains(tgtmtd)) { + return true; + } + MethodPAG tgtmpag = prePTA.getPag().getMethodPAG(tgtmtd); + AbstractMVFG tgtVfg = method2VFG.get(tgtmtd); + assert tgtVfg != null; + MethodNodeFactory tgtnf = tgtmpag.nodeFactory(); + LocalVarNode parm; + if (paramIndex == PointsToAnalysis.THIS_NODE) { + parm = (LocalVarNode) tgtnf.caseThis(); + } else { + if (tgtmtd.getParameterType(paramIndex) instanceof ReferenceType) { + parm = (LocalVarNode) tgtnf.caseParm(paramIndex); + } else { + continue; + } + } + if (tgtVfg.getCSNodes().contains(parm)) { + return true; + } + } + return false; + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/turner/OCG.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/turner/OCG.java new file mode 100644 index 00000000000..7aac6a52be5 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/turner/OCG.java @@ -0,0 +1,261 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.turner; + +import java.util.*; +import qilin.core.PTA; +import qilin.core.builder.MethodNodeFactory; +import qilin.core.pag.*; +import qilin.core.sets.PointsToSet; +import qilin.pta.PTAConfig; +import qilin.util.PTAUtils; +import sootup.core.model.SootMethod; +import sootup.core.types.ArrayType; +import sootup.core.types.PrimitiveType; +import sootup.core.types.ReferenceType; +import sootup.core.types.Type; + +// Object Containment Graph +public class OCG { + public final PTA pta; + protected final Map> pts; + public Map nodes; + private int total_node_count = 0; + private int total_edge_count = 0; + + public OCG(PTA pta) { + this.pta = pta; + this.pts = PTAUtils.calcStaticThisPTS(pta); + this.nodes = new HashMap<>(); + buildGraph(); + } + + protected void buildGraph() { + PAG pag = pta.getPag(); + pag.getAllocNodes().forEach(this::findOrCreate); + pag.getContextFields() + .forEach( + contextField -> { + AllocNode base = contextField.getBase(); + if (base instanceof ConstantNode) { + return; + } + + SparkField f = contextField.getField(); + if (f.getType() instanceof ArrayType) { + ArrayType at = (ArrayType) f.getType(); + if (at.getBaseType() instanceof PrimitiveType) { + return; + } + } + PointsToSet pts = pta.reachingObjects(contextField).toCIPointsToSet(); + for (Iterator it = pts.iterator(); it.hasNext(); ) { + AllocNode n = it.next(); + if (n instanceof ConstantNode) { + continue; + } + addEdge(findOrCreate(base), findOrCreate(n)); + } + }); + } + + public Collection allNodes() { + return nodes.values(); + } + + public int getTotalNodeCount() { + return total_node_count; + } + + public int getTotalEdgeCount() { + return total_edge_count; + } + + /** + * (1) case1: objects on OCG have successors but does not have predecessors. (1-1) factorys (1-2) + * normal uses. (2) case2: objects on OCG does not have successors. (2-1) no predecessors. (2-2) + * have predecessors. (3) othercase: objects on OCG have successors and predecessors. + */ + public void stat() { + int case1 = 0; + int total_factory = 0; + int case1_factory = 0; + int case1_normal = 0; + int case2 = 0; + int case2_noPred = 0; + int case2_hasPred = 0; + int otherCase = 0; + for (OCGNode node : nodes.values()) { + if (node.successors.size() == 0) { + case2++; + if (node.predecessors.size() == 0) { + case2_noPred++; + } else { + case2_hasPred++; + } + if (isFactoryObject(node.ir)) { + total_factory++; + } + } else if (node.predecessors.size() == 0) { + case1++; + if (isFactoryObject(node.ir)) { + case1_factory++; + // System.out.println(((AllocNode) node.ir).toString2()); + } else { + case1_normal++; + } + } else { + if (isFactoryObject(node.ir)) { + total_factory++; + } + otherCase++; + } + } + + System.out.println("#case1:" + case1); + System.out.println("#total_factory:" + total_factory); + System.out.println("#case1_factory:" + case1_factory); + System.out.println("#case1_normal:" + case1_normal); + System.out.println("#case2:" + case2); + System.out.println("#case2_noPred:" + case2_noPred); + System.out.println("#case2_hasPred:" + case2_hasPred); + System.out.println("#othercase:" + otherCase); + } + + private OCGNode findOrCreate(AllocNode ir) { + if (nodes.containsKey(ir)) { + return nodes.get(ir); + } else { + total_node_count++; + OCGNode ret = new OCGNode(ir); + nodes.put(ir, ret); + return ret; + } + } + + private boolean isNewTop(OCGNode node) { + return node.predecessors.isEmpty() && !isFactoryObject(node.ir); + } + + private boolean isNewBottom(OCGNode node) { + return node.successors.isEmpty(); + } + + public boolean isTop(AllocNode heap) { + return !nodes.containsKey(heap) || isNewTop(findOrCreate(heap)); + } + + public boolean isBottom(AllocNode heap) { + return !nodes.containsKey(heap) || isNewBottom(findOrCreate(heap)); + } + + private boolean isNotTopAndBottom(OCGNode node) { + return !isNewBottom(node) && !isNewTop(node); + } + + public boolean isCSLikely(AllocNode allocNode) { + OCGNode node = this.nodes.getOrDefault(allocNode, null); + if (node == null) { + return false; + } else { + return node.cslikely; + } + } + + public void run() { + int[] a = new int[2]; + System.out.println(PTAConfig.v().turnerConfig); + for (OCGNode node : nodes.values()) { + PTAConfig.TurnerConfig hgConfig = PTAConfig.v().turnerConfig; + if (hgConfig == PTAConfig.TurnerConfig.PHASE_TWO) { + node.cslikely = true; + } else { + node.cslikely = isNotTopAndBottom(node); + } + if (node.cslikely) { + a[1]++; + } else { + a[0]++; + } + } + for (int i = 0; i < 2; ++i) { + System.out.println("#level " + i + ": " + a[i]); + } + stat(); + } + + protected void addEdge(OCGNode pre, OCGNode succ) { + total_edge_count++; + pre.addSucc(succ); + succ.addPred(pre); + } + + public static class OCGNode { + public final AllocNode ir; + public Set successors; + public Set predecessors; + public boolean cslikely; + + public OCGNode(AllocNode ir) { + this.ir = ir; + this.cslikely = false; + this.successors = new HashSet<>(); + this.predecessors = new HashSet<>(); + } + + @Override + public String toString() { + return ir.toString(); + } + + public void addSucc(OCGNode node) { + this.successors.add(node); + } + + public void addPred(OCGNode node) { + this.predecessors.add(node); + } + } + + /** + * patterns in case1: (1) create one object and never return out of the method. (2) create one + * object and then return out (factory). + */ + boolean isFactoryObject(AllocNode heap) { + SootMethod method = heap.getMethod(); + if (method == null) { + return false; + } + Type retType = method.getReturnType(); + if (!(retType instanceof ReferenceType)) { + return false; + } + if (retType instanceof ArrayType) { + ArrayType at = (ArrayType) retType; + if (at.getBaseType() instanceof PrimitiveType) { + return false; + } + } + MethodPAG methodPAG = pta.getPag().getMethodPAG(method); + MethodNodeFactory factory = methodPAG.nodeFactory(); + Node retNode = factory.caseRet(); + PointsToSet pts = pta.reachingObjects(retNode).toCIPointsToSet(); + return pts.contains(heap); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/turner/TranEdge.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/turner/TranEdge.java new file mode 100644 index 00000000000..4d28c25e990 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/turner/TranEdge.java @@ -0,0 +1,60 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.turner; + +import java.util.Objects; + +public class TranEdge { + private final Object src; + private final Object dst; + private final DFA.TranCond tranCond; + + public TranEdge(Object s, Object d, DFA.TranCond tran) { + this.src = s; + this.dst = d; + this.tranCond = tran; + } + + public Object getSource() { + return src; + } + + public Object getTarget() { + return dst; + } + + public DFA.TranCond getTranCond() { + return tranCond; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TranEdge tranEdge = (TranEdge) o; + return Objects.equals(src, tranEdge.src) + && Objects.equals(dst, tranEdge.dst) + && tranCond == tranEdge.tranCond; + } + + @Override + public int hashCode() { + return Objects.hash(src, dst, tranCond); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/turner/Turner.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/turner/Turner.java new file mode 100644 index 00000000000..12afe945c77 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/turner/Turner.java @@ -0,0 +1,241 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.turner; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import qilin.core.PTA; +import qilin.core.pag.AllocNode; +import qilin.core.pag.PAG; +import qilin.core.pag.SparkField; +import qilin.pta.PTAConfig; +import qilin.util.PTAUtils; +import qilin.util.graph.MergedNode; +import qilin.util.graph.SCCMergedGraph; +import qilin.util.graph.TopologicalSorter; +import sootup.core.model.SootMethod; + +public class Turner { + protected OCG ocg; + private int total_node_count = 0; + private int total_edge_count = 0; + private final PTA prePTA; + private final int k; + private final int hk; + public static boolean isModular = false; + + public Turner(int k, PTA pta) { + this.k = k; + this.hk = k - 1; + this.prePTA = pta; + if (isModular) { + System.out.println("Turner with modularization ..."); + } else { + System.out.println("Turner ..."); + } + } + + //////////////////////////////////////////////////////////////////////// + public Map contxtLengthAnalysis() { + this.ocg = new OCG(prePTA); + ocg.run(); + mergeNodeAndEdgeCount(ocg.getTotalNodeCount(), ocg.getTotalEdgeCount()); + // compute level for variables in methods + Set nodes = ConcurrentHashMap.newKeySet(); + Collection reachables = prePTA.getNakedReachableMethods(); + if (isModular) { + MethodLevelCallGraph mcg = new MethodLevelCallGraph(prePTA.getCallGraph()); + final SCCMergedGraph mg = new SCCMergedGraph<>(mcg); + mystat(mg); + final TopologicalSorter> topoSorter = new TopologicalSorter<>(); + topoSorter + .sort(mg, true) + .forEach( + node -> { + for (SootMethod method : node.getContent()) { + nodes.addAll(computeCtxLevelForVariables(method, node)); + } + }); + } else { + reachables.forEach( + method -> { + nodes.addAll(computeCtxLevelForVariables(method)); + }); + } + // collect nodes and their level + Map ret1 = new HashMap<>(); + // compute level for variables/objects + reachables.forEach( + method -> { + AbstractMVFG mvfg = MethodVFG.findMethodVFG(method); + if (mvfg != null) { + for (Object obj : mvfg.getAllNodes()) { + if (nodes.contains(obj)) { + ret1.put(obj, obj instanceof AllocNode ? hk : k); + } else { + ret1.put(obj, 0); + } + } + } + }); + // compute level for fields, In our paper, we do not mention this part + // as it does not hurt too much efficiency. + PAG pag = prePTA.getPag(); + Set fields = new HashSet<>(); + Set readSet = new HashSet<>(); + Set writeSet = new HashSet<>(); + + pag.getContextFields() + .forEach( + contextField -> { + SparkField field = contextField.getField(); + fields.add(field); + if (!pag.simpleInvLookup(contextField).isEmpty()) { + writeSet.add(field); + } + if (!pag.simpleLookup(contextField).isEmpty()) { + readSet.add(field); + } + }); + + for (SparkField f : fields) { + int x = 0; + this.total_node_count += 1; + if (writeSet.contains(f) && readSet.contains(f)) { + x = k; + } + ret1.put(f, x); + } + Map ret = new HashMap<>(ret1); + if (PTAConfig.v().turnerConfig == PTAConfig.TurnerConfig.PHASE_ONE) { + Map ret2 = new HashMap<>(); + ret1.forEach( + (w, v) -> { + if (w instanceof AllocNode) { + ret2.put(w, ocg.isCSLikely((AllocNode) w) ? hk : 0); + } else { + ret2.put(w, k); + } + }); + ret = ret2; + } + System.out.println("#Node:" + this.total_node_count); + System.out.println("#Edge:" + this.total_edge_count); + statObjInOCG(ret); + return ret; + } + + private void statObjInOCG(Map ret) { + int cibyocg = 0; + int cibydfa = 0; + int csobj = 0; + int tops = 0; + int bottoms = 0; + int topandbottoms = 0; + for (AllocNode o : prePTA.getPag().getAllocNodes()) { + if (!this.ocg.isCSLikely(o)) { + cibyocg++; + } else if (ret.containsKey(o) && ret.get(o) > 0) { + csobj++; + } else { + cibydfa++; + } + if (this.ocg.isTop(o)) { + tops++; + } + if (this.ocg.isBottom(o)) { + bottoms++; + } + if (this.ocg.isTop(o) && this.ocg.isBottom(o)) { + topandbottoms++; + } + } + System.out.println("#CIByOCG:" + cibyocg); + System.out.println("#CIByDFA:" + cibydfa); + System.out.println("#CSOBJ:" + csobj); + System.out.println("#CITOP:" + tops); + System.out.println("#CIBOT:" + bottoms); + System.out.println("#CITOPBOT:" + topandbottoms); + } + + private Collection computeCtxLevelForVariables(SootMethod method) { + if (!PTAUtils.hasBody(method)) { + return Collections.emptySet(); + } else { + AbstractMVFG mvfg = MethodVFG.findOrCreateMethodVFG(prePTA, method, ocg); + mvfg.computeNodesInPrecisionLossPatterns(); + mergeNodeAndEdgeCount(mvfg.getTotalNodeCount(), mvfg.getTotalEdgeCount()); + return mvfg.getCSNodes(); + } + } + + private Collection computeCtxLevelForVariables( + SootMethod method, MergedNode sccNode) { + if (!PTAUtils.hasBody(method)) { + return Collections.emptySet(); + } else { + AbstractMVFG mvfg = ModularMVFG.findOrCreateMethodVFG(prePTA, method, ocg, sccNode); + mvfg.computeNodesInPrecisionLossPatterns(); + mergeNodeAndEdgeCount(mvfg.getTotalNodeCount(), mvfg.getTotalEdgeCount()); + return mvfg.getCSNodes(); + } + } + + private void mergeNodeAndEdgeCount(int nodeCnt, int edgeCnt) { + this.total_node_count += nodeCnt; + this.total_edge_count += edgeCnt; + } + + private void mystat(SCCMergedGraph scccg) { + int sccCnt = scccg.allNodes().size(); + int sccCntGtW = 0; + int maxScc = 0; + double avgScc = 0.0; + double avgSccGtW = 0.0; + for (MergedNode scc : scccg.allNodes()) { + int sccSize = scc.getContent().size(); + if (sccSize > maxScc) { + maxScc = sccSize; + } + avgScc += sccSize; + if (sccSize > 1) { + ++sccCntGtW; + avgSccGtW += sccSize; + } + } + avgScc /= sccCnt; + avgSccGtW /= sccCntGtW; + int[] dist = new int[maxScc + 1]; + for (MergedNode scc : scccg.allNodes()) { + int sccSize = scc.getContent().size(); + dist[sccSize]++; + } + System.out.println("#scc count:" + sccCnt); + System.out.println("#scc count (exclude singleton):" + sccCntGtW); + System.out.println("Average scc size:" + avgScc); + System.out.println("Average scc size(exclude singleton):" + avgSccGtW); + System.out.println("Maximum scc size:" + maxScc); + System.out.println("Scc size distribution (size, count):"); + for (int i = 0; i <= maxScc; ++i) { + if (dist[i] > 0) { + System.out.println(i + "," + dist[i]); + } + } + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/Global.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/Global.java new file mode 100644 index 00000000000..04081e05328 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/Global.java @@ -0,0 +1,87 @@ +package qilin.pta.toolkits.zipper; + +public class Global { + private static boolean debug = false; + private static String flow = null; + private static boolean enableWrappedFlow = true; + private static boolean enableUnwrappedFlow = true; + private static boolean isExpress = false; + public static final int UNDEFINE = -1; + private static int tst = UNDEFINE; + private static int thread = UNDEFINE; + private static float expressThreshold = 0.05f; + + public static void setDebug(final boolean debug) { + Global.debug = debug; + } + + public static boolean isDebug() { + return Global.debug; + } + + public static String getFlow() { + return Global.flow; + } + + public static void setFlow(String flow) { + Global.flow = flow; + } + + public static boolean isEnableWrappedFlow() { + return Global.enableWrappedFlow; + } + + public static void setEnableWrappedFlow(boolean enableWrappedFlow) { + Global.enableWrappedFlow = enableWrappedFlow; + } + + public static boolean isEnableUnwrappedFlow() { + return Global.enableUnwrappedFlow; + } + + public static void setEnableUnwrappedFlow(boolean enableUnwrappedFlow) { + Global.enableUnwrappedFlow = enableUnwrappedFlow; + } + + public static boolean isExpress() { + return Global.isExpress; + } + + public static void setExpress(final boolean isExpress) { + Global.isExpress = isExpress; + } + + public static int getTST() { + return Global.tst; + } + + public static void setTST(int tst) { + Global.tst = tst; + } + + public static int getThread() { + return thread; + } + + public static void setThread(int thread) { + Global.thread = thread; + } + + public static float getExpressThreshold() { + return expressThreshold; + } + + public static void setExpressThreshold(float expressThreshold) { + Global.expressThreshold = expressThreshold; + } + + private static boolean listContext = false; + + public static boolean isListContext() { + return listContext; + } + + public static void setListContext(boolean listContext) { + Global.listContext = listContext; + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/Main.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/Main.java new file mode 100644 index 00000000000..2e8c86d5964 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/Main.java @@ -0,0 +1,59 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.zipper; + +import java.util.Set; +import qilin.core.PTA; +import qilin.pta.toolkits.zipper.analysis.Zipper; +import qilin.util.ANSIColor; +import qilin.util.Stopwatch; +import sootup.core.model.SootMethod; + +public class Main { + + public static void run(PTA pta, Set zipperPCMOutput) { + int numThreads = Runtime.getRuntime().availableProcessors(); + Global.setThread(numThreads); + Global.setExpress(false); + String zipperStr = Global.isExpress() ? "Zipper-e" : "Zipper"; + System.out.println( + ANSIColor.BOLD + ANSIColor.YELLOW + zipperStr + " starts ..." + ANSIColor.RESET); + String flows = Global.getFlow() != null ? Global.getFlow() : "Direct+Wrapped+Unwrapped"; + System.out.println( + "Precision loss patterns: " + ANSIColor.BOLD + ANSIColor.GREEN + flows + ANSIColor.RESET); + Zipper.outputNumberOfClasses(pta); + Stopwatch zipperTimer = Stopwatch.newAndStart("Zipper Timer"); + Zipper zipper = new Zipper(pta); + Set pcm = zipper.analyze(); + zipperTimer.stop(); + System.out.print( + ANSIColor.BOLD + + ANSIColor.YELLOW + + zipperStr + + " finishes, analysis time: " + + ANSIColor.RESET); + System.out.print(ANSIColor.BOLD + ANSIColor.GREEN); + System.out.printf("%.2fs", zipperTimer.elapsed()); + System.out.println(ANSIColor.RESET); + + System.out.println("Writing Zipper precision-critical methods ...\n"); + System.out.println(); + zipperPCMOutput.addAll(pcm); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/ReadMe.txt b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/ReadMe.txt new file mode 100644 index 00000000000..f1ad774f9dc --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/ReadMe.txt @@ -0,0 +1,6 @@ +Get its source from https://github.com/silverbullettt/zipper.git +Commit: b83b0382580eed3e93e88b9bc07cfe27c2c2af01 +Replace original Obj with AllocNode; +Replace original Variable with VarNode; +Replace original Method/InstanceMethod/StaticMethod with SootMethod; +Replace original Field with SparkField; \ No newline at end of file diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/analysis/PotentialContextElement.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/analysis/PotentialContextElement.java new file mode 100644 index 00000000000..ba5863de396 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/analysis/PotentialContextElement.java @@ -0,0 +1,208 @@ +package qilin.pta.toolkits.zipper.analysis; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import qilin.core.PTA; +import qilin.core.builder.MethodNodeFactory; +import qilin.core.pag.AllocNode; +import qilin.core.pag.ContextMethod; +import qilin.core.pag.VarNode; +import qilin.core.sets.PointsToSet; +import qilin.pta.toolkits.common.OAG; +import qilin.pta.toolkits.zipper.Global; +import qilin.util.collect.SetFactory; +import qilin.util.graph.MergedNode; +import qilin.util.graph.SCCMergedGraph; +import qilin.util.graph.TopologicalSorter; +import sootup.core.model.SootMethod; +import sootup.core.types.Type; + +/** + * For each object o, this class compute the set of methods which o could potentially be their + * context element. + * + *

Conversely, for each method m, this class compute the set of objects which could potentially + * be its context element. + */ +public class PotentialContextElement { + private final PTA pta; + // This map maps each object to the methods invoked on it. + // For instance methods, they are the methods whose receiver is the object. + // For static methods, they are the methods reachable from instance methods. + private Map> invokedMethods; + private Map> obj2invokedMethods; + private final Map> typePCEMethods; + private final Map> pceOfMap; + + private final OAG oag; + private final Map> typeAllocatees; + private final Map> allocateeMap; + + PotentialContextElement(final PTA pta, final OAG oag) { + this.pta = pta; + this.oag = oag; + this.typePCEMethods = new ConcurrentHashMap<>(); + this.obj2invokedMethods = new ConcurrentHashMap<>(); + this.pceOfMap = new ConcurrentHashMap<>(); + this.typeAllocatees = new ConcurrentHashMap<>(); + this.allocateeMap = new ConcurrentHashMap<>(); + this.init(oag); + } + + public Set PCEMethodsOf(final AllocNode obj) { + return this.pceOfMap.getOrDefault(obj, Collections.emptySet()); + } + + /** + * @param type + * @return PCE methods of the objects of given type. + */ + public Set PCEMethodsOf(final Type type) { + if (!this.typePCEMethods.containsKey(type)) { + final Set methods = ConcurrentHashMap.newKeySet(); + pta.getPag().getAllocNodes().stream() + .filter(o -> o.getType().equals(type)) + .forEach(obj -> methods.addAll(this.PCEMethodsOf(obj))); + this.typePCEMethods.put(type, methods); + } + return this.typePCEMethods.getOrDefault(type, Collections.emptySet()); + } + + /** Compute PCE methods for each objects. */ + private void init(final OAG oag) { + final SCCMergedGraph mg = new SCCMergedGraph<>(oag); + final TopologicalSorter> topoSorter = new TopologicalSorter<>(); + final SetFactory setFactory = new SetFactory<>(); + final SetFactory setFactory2 = new SetFactory<>(); + this.buildMethodsInvokedOnObjects(); + this.invokedMethods = new HashMap<>(); + topoSorter + .sort(mg, true) + .forEach( + node -> { + final Set methods = ConcurrentHashMap.newKeySet(); + methods.addAll(setFactory.get(this.getPCEMethods(node, mg))); + final Set allocatees = ConcurrentHashMap.newKeySet(); + allocatees.addAll(setFactory2.get(this.getAllocatees(node, mg))); + node.getContent() + .forEach( + obj -> { + pceOfMap.put(obj, methods); + allocateeMap.put(obj, allocatees); + }); + }); + this.invokedMethods = null; + if (Global.isDebug()) { + this.computePCEObjects(); + } + + oag.allNodes() + .forEach( + obj -> { + final Type type = obj.getType(); + this.typeAllocatees.putIfAbsent(type, new HashSet<>()); + this.typeAllocatees.get(type).addAll(this.allocateesOf(obj)); + }); + } + + private Set getAllocatees( + final MergedNode node, final SCCMergedGraph mg) { + final Set allocatees = new HashSet<>(); + mg.succsOf(node) + .forEach( + n -> { + // direct allocatees + allocatees.addAll(n.getContent()); + // indirect allocatees, here, it does not require to traverse all heaps in + // n.getContent() + // because of lines 104-107. + final AllocNode o = n.getContent().iterator().next(); + allocatees.addAll(this.allocateesOf(o)); + }); + final AllocNode obj = node.getContent().iterator().next(); + if (node.getContent().size() > 1 || oag.succsOf(obj).contains(obj)) { + // The merged node is a true SCC + allocatees.addAll(node.getContent()); + } + return allocatees; + } + + private Set allocateesOf(final AllocNode obj) { + return this.allocateeMap.getOrDefault(obj, Collections.emptySet()); + } + + public Set allocateesOf(final Type type) { + return this.typeAllocatees.getOrDefault(type, Collections.emptySet()); + } + + private Set getPCEMethods( + final MergedNode node, final SCCMergedGraph mg) { + final Set methods = new HashSet<>(); + mg.succsOf(node) + .forEach( + n -> { + final AllocNode o2 = n.getContent().iterator().next(); + methods.addAll(this.PCEMethodsOf(o2)); + }); + node.getContent().forEach(o -> methods.addAll(this.invokedMethodsOf(o))); + return methods; + } + + public Set methodsInvokedOn(final AllocNode obj) { + return this.obj2invokedMethods.getOrDefault(obj, Collections.emptySet()); + } + + private void buildMethodsInvokedOnObjects() { + this.obj2invokedMethods = new HashMap<>(); + pta.getNakedReachableMethods().stream() + .filter(m -> !m.isStatic()) + .forEach( + instMtd -> { + MethodNodeFactory mthdNF = pta.getPag().getMethodPAG(instMtd).nodeFactory(); + VarNode thisVar = mthdNF.caseThis(); + PointsToSet pts = pta.reachingObjects(thisVar).toCIPointsToSet(); + for (Iterator it = pts.iterator(); it.hasNext(); ) { + AllocNode obj = it.next(); + obj2invokedMethods.computeIfAbsent(obj, k -> new HashSet<>()).add(instMtd); + } + }); + } + + private Set invokedMethodsOf(final AllocNode obj) { + if (!this.invokedMethods.containsKey(obj)) { + final Set methods = new HashSet<>(); + final Queue queue = new LinkedList<>(this.methodsInvokedOn(obj)); + while (!queue.isEmpty()) { + final SootMethod method = queue.poll(); + methods.add(method); + pta.getCallGraph() + .edgesOutOf(new ContextMethod(method, pta.emptyContext())) + .forEachRemaining( + edge -> { + SootMethod callee = edge.getTgt().method(); + if (callee.isStatic() && !methods.contains(callee)) { + queue.offer(callee); + } + }); + } + this.invokedMethods.put(obj, methods); + } + return this.invokedMethods.get(obj); + } + + private void computePCEObjects() { + final Map> pceObjs = new HashMap<>(); + pta.getPag() + .getAllocNodes() + .forEach( + obj -> + this.PCEMethodsOf(obj) + .forEach( + method -> { + if (!pceObjs.containsKey(method)) { + pceObjs.put(method, new HashSet<>()); + } + pceObjs.get(method).add(obj); + })); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/analysis/Zipper.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/analysis/Zipper.java new file mode 100644 index 00000000000..c7f2eddbf97 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/analysis/Zipper.java @@ -0,0 +1,349 @@ +package qilin.pta.toolkits.zipper.analysis; + +import static qilin.util.ANSIColor.color; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; +import qilin.core.PTA; +import qilin.core.pag.*; +import qilin.pta.toolkits.common.OAG; +import qilin.pta.toolkits.common.ToolUtil; +import qilin.pta.toolkits.zipper.Global; +import qilin.pta.toolkits.zipper.flowgraph.FlowAnalysis; +import qilin.pta.toolkits.zipper.flowgraph.ObjectFlowGraph; +import qilin.util.ANSIColor; +import qilin.util.Stopwatch; +import qilin.util.graph.ConcurrentDirectedGraphImpl; +import sootup.core.model.SootClass; +import sootup.core.model.SootMethod; +import sootup.core.types.ClassType; +import sootup.core.types.Type; + +/** + * Main class of Zipper, which computes precision-critical methods in the program being analyzed. + */ +public class Zipper { + private final PTA pta; + private final PotentialContextElement pce; + private final ObjectFlowGraph ofg; + private final AtomicInteger analyzedClasses = new AtomicInteger(0); + private final AtomicInteger totalPFGNodes = new AtomicInteger(0); + private final AtomicInteger totalPFGEdges = new AtomicInteger(0); + private final ConcurrentDirectedGraphImpl overallPFG = new ConcurrentDirectedGraphImpl<>(); + private final Map methodPts; + private final Map> pcmMap = new ConcurrentHashMap<>(1024); + + public Zipper(PTA pta) { + this.pta = pta; + OAG oag = new OAG(pta); + oag.build(); + System.out.println("#OAG:" + oag.allNodes().size()); + this.pce = new PotentialContextElement(pta, oag); + this.ofg = buildObjectFlowGraph(); + this.methodPts = getMethodPointsToSize(); + } + + public static void outputNumberOfClasses(PTA pta) { + int nrClasses = + (int) pta.getPag().getAllocNodes().stream().map(AllocNode::getType).distinct().count(); + System.out.println( + "#classes: " + ANSIColor.BOLD + ANSIColor.GREEN + nrClasses + ANSIColor.RESET); + System.out.println(); + } + + public int numberOfOverallPFGNodes() { + return overallPFG.allNodes().size(); + } + + public int numberOfOverallPFGEdges() { + int nrEdges = 0; + for (Node node : overallPFG.allNodes()) { + nrEdges += overallPFG.succsOf(node).size(); + } + return nrEdges; + } + + public ObjectFlowGraph buildObjectFlowGraph() { + Stopwatch ofgTimer = Stopwatch.newAndStart("Object Flow Graph Timer"); + System.out.println("Building OFG (Object Flow Graph) ... "); + ObjectFlowGraph ofg = new ObjectFlowGraph(pta); + ofgTimer.stop(); + System.out.println(ofgTimer); + outputObjectFlowGraphSize(ofg); + return ofg; + } + + public static void outputObjectFlowGraphSize(ObjectFlowGraph ofg) { + int nrNodes = ofg.allNodes().size(); + int nrEdges = 0; + for (Node node : ofg.allNodes()) { + nrEdges += ofg.outEdgesOf(node).size(); + } + + System.out.println( + "#nodes in OFG: " + ANSIColor.BOLD + ANSIColor.GREEN + nrNodes + ANSIColor.RESET); + System.out.println( + "#edges in OFG: " + ANSIColor.BOLD + ANSIColor.GREEN + nrEdges + ANSIColor.RESET); + System.out.println(); + } + + /** @return set of precision-critical methods in the program */ + public Set analyze() { + reset(); + System.out.println( + "Building PFGs (Pollution Flow Graphs) and computing precision-critical methods ..."); + List types = + pta.getPag().getAllocNodes().stream() + .map(AllocNode::getType) + .distinct() + .sorted(Comparator.comparing(Type::toString)) + .filter(t -> t instanceof ClassType) + .map(t -> (ClassType) t) + .collect(Collectors.toList()); + if (Global.getThread() == Global.UNDEFINE) { + computePCM(types); + } else { + computePCMConcurrent(types, Global.getThread()); + } + System.out.println( + "#avg. nodes in PFG: " + + ANSIColor.BOLD + + ANSIColor.GREEN + + Math.round(totalPFGNodes.floatValue() / analyzedClasses.get()) + + ANSIColor.RESET); + System.out.println( + "#avg. edges in PFG: " + + ANSIColor.BOLD + + ANSIColor.GREEN + + Math.round(totalPFGEdges.floatValue() / analyzedClasses.get()) + + ANSIColor.RESET); + System.out.println("#Node:" + totalPFGNodes.intValue()); + System.out.println("#Edge:" + totalPFGEdges.intValue()); + System.out.println("#Node2:" + numberOfOverallPFGNodes()); + System.out.println("#Edge2:" + numberOfOverallPFGEdges()); + System.out.println(); + + Set pcm = collectAllPrecisionCriticalMethods(pcmMap, computePCMThreshold()); + System.out.println( + "#Precision-critical methods: " + + ANSIColor.BOLD + + ANSIColor.GREEN + + pcm.size() + + ANSIColor.RESET); + return pcm; + } + + private void computePCM(List types) { + FlowAnalysis fa = new FlowAnalysis(pta, pce, ofg); + types.forEach(type -> analyze(type, fa)); + } + + private void computePCMConcurrent(List types, int nThread) { + ExecutorService executorService = Executors.newFixedThreadPool(nThread); + types.forEach( + type -> + executorService.execute( + () -> { + FlowAnalysis fa = new FlowAnalysis(pta, pce, ofg); + analyze(type, fa); + })); + executorService.shutdown(); + try { + executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + /** + * @param type + * @param fa Compute the set of precision-critical methods for a class/type and add these methods + * to the pcm collection. + */ + private void analyze(ClassType type, FlowAnalysis fa) { + if (Global.isDebug()) { + System.out.println("----------------------------------------"); + } + // System.out.println(color(YELLOW, "Zipper: analyzing ") + type); + + // Obtain all methods of type (including inherited methods) + Set ms = + pta.getPag().getAllocNodes().stream() + .filter(o -> o.getType().equals(type)) + .map(pce::methodsInvokedOn) + .flatMap(Collection::stream) + .collect(Collectors.toSet()); + // Obtain IN methods + Set inms = + ms.stream() + .filter(m -> !m.isPrivate()) + .filter( + m -> + ToolUtil.getParameters(pta.getPag(), m).stream() + .anyMatch(p -> !pta.reachingObjects(p).toCIPointsToSet().isEmpty())) + .collect(Collectors.toSet()); + + // Obtain OUT methods + Set outms = new HashSet<>(); + ms.stream() + .filter(m -> !m.isPrivate()) + .filter( + m -> + ToolUtil.getRetVars(pta.getPag(), m).stream() + .anyMatch(r -> !pta.reachingObjects(r).toCIPointsToSet().isEmpty())) + .forEach(outms::add); + + // OUT methods of inner classes and special access$ methods + // are also considered as the OUT methods of current type + pce.PCEMethodsOf(type).stream() + .filter(m -> !m.isPrivate() && !m.isStatic()) + .filter(m -> isInnerType(m.getDeclaringClassType(), type)) + .forEach(outms::add); + pce.PCEMethodsOf(type).stream() + .filter(m -> !m.isPrivate() && !m.isStatic()) + .filter(m -> m.getDeclaringClassType().equals(type) && m.toString().contains("access$")) + .forEach(outms::add); + + if (Global.isDebug()) { + System.out.println(color(ANSIColor.YELLOW, "In methods:")); + inms.stream() + .sorted(Comparator.comparing(SootMethod::toString)) + .forEach(m -> System.out.println(" " + m)); + System.out.println(color(ANSIColor.YELLOW, "Out methods:")); + outms.stream() + .sorted(Comparator.comparing(SootMethod::toString)) + .forEach(m -> System.out.println(" " + m)); + } + + fa.initialize(type, inms, outms); + inms.forEach(fa::analyze); + Set flowNodes = fa.getFlowNodes(); + Set precisionCriticalMethods = getPrecisionCriticalMethods(type, flowNodes); + if (Global.isDebug()) { + if (!precisionCriticalMethods.isEmpty()) { + System.out.println(color(ANSIColor.BLUE, "Flow found: ") + type); + } + } + mergeAnalysisResults( + type, fa.numberOfPFGNodes(), fa.numberOfPFGEdges(), precisionCriticalMethods); + mergeSinglePFG(fa.getPFG()); + fa.clear(); + } + + /** + * @param pInner potential inner class + * @param pOuter potential outer class + * @return whether pInner is an inner class of pOuter + */ + public boolean isInnerType(final ClassType pInner, ClassType pOuter) { + final String pInnerStr = pInner.toString(); + while (!pInnerStr.startsWith(pOuter.toString() + "$")) { + SootClass sc = pta.getView().getClass(pOuter).get(); + if (sc.hasSuperclass()) { + pOuter = sc.getSuperclass().get(); + } else { + return false; + } + } + return true; + } + + private void mergeSinglePFG(ConcurrentDirectedGraphImpl pfg) { + for (Node node : pfg.allNodes()) { + this.overallPFG.addNode(node); + for (Node succ : pfg.succsOf(node)) { + this.overallPFG.addEdge(node, succ); + } + } + } + + private void mergeAnalysisResults( + Type type, int nrPFGNodes, int nrPFGEdges, Set precisionCriticalMethods) { + analyzedClasses.incrementAndGet(); + totalPFGNodes.addAndGet(nrPFGNodes); + totalPFGEdges.addAndGet(nrPFGEdges); + pcmMap.put(type, new ArrayList<>(precisionCriticalMethods)); + } + + private Set collectAllPrecisionCriticalMethods( + Map> pcmMap, int pcmThreshold) { + System.out.println("PCM Threshold:" + pcmThreshold); + Set pcm = new HashSet<>(); + pcmMap.forEach( + (type, pcms) -> { + if (Global.isExpress() && getAccumulativePointsToSetSize(pcms) > pcmThreshold) { + System.out.println( + "type: " + type + ", accumulativePTSize: " + getAccumulativePointsToSetSize(pcms)); + return; + } + pcm.addAll(pcms); + }); + return pcm; + } + + private int computePCMThreshold() { + // Use points-to size of whole program as denominator + int totalPTSSize = 0; + for (ValNode var : pta.getPag().getValNodes()) { + if (var instanceof VarNode) { + VarNode varNode = (VarNode) var; + // Collection pts = ToolUtil.pointsToSetOf(pta, varNode); + totalPTSSize += pta.reachingObjects(varNode).toCIPointsToSet().size(); + } + } + return (int) (Global.getExpressThreshold() * totalPTSSize); + } + + private Set getPrecisionCriticalMethods(Type type, Set nodes) { + return nodes.stream() + .map(this::node2ContainingMethod) + .filter(Objects::nonNull) + .filter(pce.PCEMethodsOf(type)::contains) + .collect(Collectors.toSet()); + } + + private SootMethod node2ContainingMethod(Node node) { + if (node instanceof LocalVarNode) { + LocalVarNode lvn = (LocalVarNode) node; + return lvn.getMethod(); + } else { + ContextField ctxField = (ContextField) node; + return ctxField.getBase().getMethod(); + } + } + + private void reset() { + analyzedClasses.set(0); + totalPFGNodes.set(0); + totalPFGEdges.set(0); + pcmMap.clear(); + } + + private Map getMethodPointsToSize() { + Map results = new HashMap<>(); + for (ValNode valnode : pta.getPag().getValNodes()) { + if (!(valnode instanceof LocalVarNode)) { + continue; + } + LocalVarNode lvn = (LocalVarNode) valnode; + SootMethod inMethod = lvn.getMethod(); + int ptSize = ToolUtil.pointsToSetSizeOf(pta, lvn); + if (results.containsKey(inMethod)) { + int oldValue = results.get(inMethod); + results.replace(inMethod, oldValue, oldValue + ptSize); + } else { + results.put(inMethod, ptSize); + } + } + return results; + } + + private long getAccumulativePointsToSetSize(Collection methods) { + return methods.stream().mapToInt(methodPts::get).sum(); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/cases/CaseA.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/cases/CaseA.java new file mode 100644 index 00000000000..68fb22135c0 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/cases/CaseA.java @@ -0,0 +1,19 @@ +package qilin.pta.toolkits.zipper.cases; + +// NO IN , NO OUT, CASE 4. +public class CaseA { + static class A { + Object f; + } + + A create() { + return new A(); + } + + void foo() { + Object o = new Object(); + A a = create(); + a.f = o; + o = a.f; + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/cases/CaseB.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/cases/CaseB.java new file mode 100644 index 00000000000..301fa7d0d63 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/cases/CaseB.java @@ -0,0 +1,25 @@ +package qilin.pta.toolkits.zipper.cases; + +// No Out, case 2 +public class CaseB { + static class A { + Object f; + + void bar(Object q) { + this.f = q; + } + } + + void foo() { + Object oy = new Object(); + Object oz = new Object(); + + A a = new A(); + a.bar(oy); + Object o = a.f; + + // A a1 = new A(); + // a1.bar(oz); + // Object o1 = a1.f; + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/cases/CaseC.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/cases/CaseC.java new file mode 100644 index 00000000000..5e1e4e5fbdb --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/cases/CaseC.java @@ -0,0 +1,35 @@ +package qilin.pta.toolkits.zipper.cases; + +public class CaseC { + static class A { + Object f; + } + + A f; + + // case 2; + CaseC() { + A a = new A(); + this.f = a; + } + + // does not consider this_variable. + A getF() { + A r = this.f; + return r; + } + + void foo() { + Object o1 = new Object(); + CaseC c1 = new CaseC(); + A a1 = c1.getF(); + a1.f = o1; + Object o1x = a1.f; + + // Object o2 = new Object(); + // CaseC c2 = new CaseC(); + // A a2 = c2.getF(); + // a2.f = o2; + // Object o2x = a2.f; + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/cases/CaseD.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/cases/CaseD.java new file mode 100644 index 00000000000..db452344624 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/cases/CaseD.java @@ -0,0 +1,21 @@ +package qilin.pta.toolkits.zipper.cases; + +public class CaseD { + static class A { + Object f, g; + } + + Object id(Object p) { + A a = new A(); + a.f = p; + Object r = a.f; + return r; + } + + Object fakeId(Object p) { + A a = new A(); + a.f = p; + a.g = new Object(); + return a.g; + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/flowgraph/Edge.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/flowgraph/Edge.java new file mode 100644 index 00000000000..1438db9c1d0 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/flowgraph/Edge.java @@ -0,0 +1,61 @@ +package qilin.pta.toolkits.zipper.flowgraph; + +import java.util.Objects; +import qilin.core.pag.Node; + +public class Edge { + private final Kind kind; + private final Node source; + private final Node target; + private final int hashCode; + + public Edge(final Kind kind, final Node source, final Node target) { + this.kind = kind; + this.source = source; + this.target = target; + this.hashCode = Objects.hash(kind, source, target); + } + + public Kind getKind() { + return this.kind; + } + + public Node getSource() { + return this.source; + } + + public Node getTarget() { + return this.target; + } + + @Override + public String toString() { + return this.kind + ": " + this.source + " --> " + this.target; + } + + @Override + public int hashCode() { + return this.hashCode; + } + + @Override + public boolean equals(final Object other) { + if (this.isOFGEdge()) { + return this == other; + } + if (this == other) { + return true; + } + if (!(other instanceof Edge)) { + return false; + } + final Edge otherEdge = (Edge) other; + return this.kind.equals(otherEdge.kind) + && this.source.equals(otherEdge.source) + && this.target.equals(otherEdge.target); + } + + private boolean isOFGEdge() { + return this.kind != Kind.WRAPPED_FLOW && this.kind != Kind.UNWRAPPED_FLOW; + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/flowgraph/FlowAnalysis.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/flowgraph/FlowAnalysis.java new file mode 100644 index 00000000000..e952716e526 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/flowgraph/FlowAnalysis.java @@ -0,0 +1,296 @@ +package qilin.pta.toolkits.zipper.flowgraph; + +import static qilin.util.ANSIColor.color; + +import java.util.*; +import java.util.stream.Collectors; +import qilin.core.PTA; +import qilin.core.pag.*; +import qilin.pta.toolkits.common.ToolUtil; +import qilin.pta.toolkits.zipper.Global; +import qilin.pta.toolkits.zipper.analysis.PotentialContextElement; +import qilin.util.ANSIColor; +import qilin.util.graph.ConcurrentDirectedGraphImpl; +import qilin.util.graph.Reachability; +import sootup.core.jimple.basic.Value; +import sootup.core.jimple.common.expr.AbstractInstanceInvokeExpr; +import sootup.core.jimple.common.expr.AbstractInvokeExpr; +import sootup.core.jimple.common.stmt.JAssignStmt; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.SootMethod; +import sootup.core.types.ReferenceType; +import sootup.core.types.Type; + +public class FlowAnalysis { + private final PTA pta; + private final PotentialContextElement pce; + private final ObjectFlowGraph objectFlowGraph; + + private Type currentType; + private Set inVars; + private Set outNodes; + private Set visitedNodes; + private Map> wuEdges; + private ConcurrentDirectedGraphImpl pollutionFlowGraph; + private Reachability reachability; + + public FlowAnalysis(PTA pta, PotentialContextElement pce, ObjectFlowGraph ofg) { + this.pta = pta; + this.pce = pce; + this.objectFlowGraph = ofg; + } + + public void initialize(Type type, Set inms, Set outms) { + currentType = type; + inVars = + inms.stream() + .map(m -> ToolUtil.getParameters(pta.getPag(), m)) + .flatMap(Collection::stream) + .collect(Collectors.toSet()); + outNodes = + outms.stream() + .map(m -> ToolUtil.getRetVars(pta.getPag(), m)) + .flatMap(Collection::stream) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + visitedNodes = new HashSet<>(); + wuEdges = new HashMap<>(); + pollutionFlowGraph = new ConcurrentDirectedGraphImpl<>(); + reachability = new Reachability<>(pollutionFlowGraph); + } + + public void analyze(SootMethod startMethod) { + for (VarNode param : ToolUtil.getParameters(pta.getPag(), startMethod)) { + if (param != null) { + dfs(param); + } else { + if (Global.isDebug()) { + System.out.println(param + " is absent in the flow graph."); + } + } + } + + if (Global.isDebug()) { + Set outMethods = new HashSet<>(); + for (VarNode param : ToolUtil.getParameters(pta.getPag(), startMethod)) { + if (param != null) { + for (Node outNode : outNodes) { + if (reachability.reachableNodesFrom(param).contains(outNode)) { + LocalVarNode outVarNode = (LocalVarNode) outNode; + outMethods.add(outVarNode.getMethod()); + } + } + } + } + System.out.println(color(ANSIColor.GREEN, "In method: ") + startMethod); + System.out.println(color(ANSIColor.GREEN, "Out methods: ") + outMethods); + } + } + + public Set getFlowNodes() { + Set results = new HashSet<>(); + for (Node outNode : outNodes) { + if (pollutionFlowGraph.allNodes().contains(outNode)) { + results.addAll(reachability.nodesReach(outNode)); + } + } + return results; + } + + public int numberOfPFGNodes() { + return pollutionFlowGraph.allNodes().size(); + } + + public int numberOfPFGEdges() { + int nrEdges = 0; + for (Node node : pollutionFlowGraph.allNodes()) { + nrEdges += pollutionFlowGraph.succsOf(node).size(); + } + return nrEdges; + } + + public ConcurrentDirectedGraphImpl getPFG() { + return pollutionFlowGraph; + } + + public void clear() { + currentType = null; + inVars = null; + outNodes = null; + visitedNodes = null; + wuEdges = null; + pollutionFlowGraph = null; + reachability = null; + } + + // a bit more complicated than the algorithm in TOPLAS'20 + private void dfs(Node node) { + if (Global.isDebug()) { + System.out.println(color(ANSIColor.BLUE, "Node ") + node); + } + if (visitedNodes.contains(node)) { // node has been visited + if (Global.isDebug()) { + System.out.println(color(ANSIColor.RED, "Visited node: ") + node); + } + } else { + visitedNodes.add(node); + pollutionFlowGraph.addNode(node); + // add unwrapped flow edges + if (Global.isEnableUnwrappedFlow()) { + if (node instanceof VarNode) { + VarNode var = (VarNode) node; + Collection varPts = pta.reachingObjects(var).toCIPointsToSet().toCollection(); + // Optimization: approximate unwrapped flows to make + // Zipper and pointer analysis run faster + pta.getCgb() + .getReceiverToSitesMap() + .getOrDefault(var, Collections.emptySet()) + .forEach( + vcs -> { + Stmt callsiteStmt = vcs.getUnit(); + AbstractInvokeExpr invo = callsiteStmt.getInvokeExpr(); + if (!(invo instanceof AbstractInstanceInvokeExpr)) { + return; + } + if (callsiteStmt instanceof JAssignStmt) { + JAssignStmt assignStmt = (JAssignStmt) callsiteStmt; + Value lv = assignStmt.getLeftOp(); + if (!(lv.getType() instanceof ReferenceType)) { + return; + } + final VarNode to = (VarNode) pta.getPag().findValNode(lv, var.getMethod()); + if (outNodes.contains(to)) { + for (VarNode inVar : inVars) { + if (!Collections.disjoint( + pta.reachingObjects(inVar).toCIPointsToSet().toCollection(), + varPts)) { + Edge unwrappedEdge = new Edge(Kind.UNWRAPPED_FLOW, node, to); + addWUEdge(node, unwrappedEdge); + break; + } + } + } + } + }); + } + } + List nextEdges = new ArrayList<>(); + for (Edge edge : outEdgesOf(node)) { + switch (edge.getKind()) { + case UNWRAPPED_FLOW: + case LOCAL_ASSIGN: + { + nextEdges.add(edge); + } + break; + case INTERPROCEDURAL_ASSIGN: + case INSTANCE_LOAD: + case WRAPPED_FLOW: + { + // next must be a variable + LocalVarNode next = (LocalVarNode) edge.getTarget(); + SootMethod inMethod = next.getMethod(); + // Optimization: filter out some potential spurious flows due to + // the imprecision of context-insensitive pre-analysis, which + // helps improve the performance of Zipper and pointer analysis. + if (pce.PCEMethodsOf(currentType).contains(inMethod)) { + nextEdges.add(edge); + } + } + break; + case INSTANCE_STORE: + { + ContextField next = (ContextField) edge.getTarget(); + AllocNode base = next.getBase(); + if (base.getType().equals(currentType)) { + // add wrapped flow edges to this variable + if (Global.isEnableWrappedFlow()) { + methodsInvokedOn(currentType).stream() + .map( + m -> + ToolUtil.getThis( + pta.getPag(), m)) // filter this variable of native methods + .map(n -> new Edge(Kind.WRAPPED_FLOW, next, n)) + .forEach(e -> addWUEdge(next, e)); + } + nextEdges.add(edge); + } else if (pce.allocateesOf(currentType).contains(base)) { + // Optimization, similar as above. + if (Global.isEnableWrappedFlow()) { + Set r = new HashSet<>(); + AllocNode mBase = (AllocNode) pta.parameterize(base, pta.emptyContext()); + pta.getPag() + .allocLookup(mBase) + .forEach( + v -> { + if (v instanceof ContextVarNode) { + ContextVarNode cvn = (ContextVarNode) v; + if (cvn.base() instanceof LocalVarNode) { + LocalVarNode lvn = (LocalVarNode) cvn.base(); + if (!lvn.isThis()) { + r.add(lvn); + } + } + } + }); + Iterator it = r.iterator(); + if (it.hasNext()) { + Node assigned = r.iterator().next(); + if (assigned != null) { + Edge e = new Edge(Kind.WRAPPED_FLOW, next, assigned); + addWUEdge(next, e); + } + } + } + nextEdges.add(edge); + } + } + break; + default: + { + throw new RuntimeException("Unknown edge: " + edge); + } + } + } + for (Edge nextEdge : nextEdges) { + Node nextNode = nextEdge.getTarget(); + pollutionFlowGraph.addEdge(node, nextNode); + dfs(nextNode); + } + } + } + + private void addWUEdge(Node sourceNode, Edge edge) { + wuEdges.computeIfAbsent(sourceNode, k -> new HashSet<>()).add(edge); + } + + private Collection methodsInvokedOn(Type type) { + return pta.getPag().getAllocNodes().stream() + .filter(o -> o.getType().equals(type)) + .map(pce::methodsInvokedOn) + .flatMap(Collection::stream) + .collect(Collectors.toSet()); + } + + /** + * @param node + * @return out edges of node from OFG, and wuEdges, if present + */ + private Set outEdgesOf(Node node) { + Set outEdges = objectFlowGraph.outEdgesOf(node); + if (wuEdges.containsKey(node)) { + outEdges = new HashSet<>(outEdges); + outEdges.addAll(wuEdges.get(node)); + } + return outEdges; + } + + private void outputPollutionFlowGraphSize() { + int nrNodes = pollutionFlowGraph.allNodes().size(); + int nrEdges = 0; + for (Node node : pollutionFlowGraph.allNodes()) { + nrEdges += pollutionFlowGraph.succsOf(node).size(); + } + System.out.printf("#Size of PFG of %s: %d nodes, %d edges.\n", currentType, nrNodes, nrEdges); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/flowgraph/IObjectFlowGraph.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/flowgraph/IObjectFlowGraph.java new file mode 100644 index 00000000000..777dc55a603 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/flowgraph/IObjectFlowGraph.java @@ -0,0 +1,10 @@ +package qilin.pta.toolkits.zipper.flowgraph; + +import java.util.Set; +import qilin.core.pag.Node; + +public interface IObjectFlowGraph { + Set outEdgesOf(final Node p0); + + Set allNodes(); +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/flowgraph/Kind.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/flowgraph/Kind.java new file mode 100644 index 00000000000..a7df60b694f --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/flowgraph/Kind.java @@ -0,0 +1,10 @@ +package qilin.pta.toolkits.zipper.flowgraph; + +public enum Kind { + LOCAL_ASSIGN, + INTERPROCEDURAL_ASSIGN, + INSTANCE_LOAD, + INSTANCE_STORE, + WRAPPED_FLOW, + UNWRAPPED_FLOW; +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/flowgraph/ObjectFlowGraph.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/flowgraph/ObjectFlowGraph.java new file mode 100644 index 00000000000..f18e390a444 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/flowgraph/ObjectFlowGraph.java @@ -0,0 +1,138 @@ +package qilin.pta.toolkits.zipper.flowgraph; + +import java.util.*; +import qilin.core.PTA; +import qilin.core.builder.MethodNodeFactory; +import qilin.core.pag.*; +import qilin.pta.toolkits.zipper.Global; +import sootup.core.jimple.basic.Value; +import sootup.core.jimple.common.expr.AbstractInstanceInvokeExpr; +import sootup.core.jimple.common.expr.AbstractInvokeExpr; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.SootMethod; + +public class ObjectFlowGraph implements IObjectFlowGraph { + private final PTA pta; + private Map> outEdges; + + public ObjectFlowGraph(PTA pta) { + this.pta = pta; + init(); + } + + @Override + public Set outEdgesOf(Node node) { + return outEdges.getOrDefault(node, Collections.emptySet()); + } + + @Override + public Set allNodes() { + return outEdges.keySet(); + } + + private boolean localVarBase(ValNode valNode) { + if (valNode instanceof ContextVarNode) { + ContextVarNode cvn = (ContextVarNode) valNode; + return cvn.base() instanceof LocalVarNode; + } else { + return valNode instanceof LocalVarNode; + } + } + + private LocalVarNode fetchLocalVar(ValNode valNode) { + if (valNode instanceof ContextVarNode) { + ContextVarNode cvn = (ContextVarNode) valNode; + if (cvn.base() instanceof LocalVarNode) { + return (LocalVarNode) cvn.base(); + } + } else if (valNode instanceof LocalVarNode) { + return (LocalVarNode) valNode; + } + return null; + } + + private LocalVarNode fetchVar(ValNode valNode) { + if (valNode instanceof ContextVarNode) { + ContextVarNode cvn = (ContextVarNode) valNode; + VarNode base = cvn.base(); + if (base instanceof LocalVarNode) { + return (LocalVarNode) base; + } + } else if (valNode instanceof LocalVarNode) { + return (LocalVarNode) valNode; + } + return null; + } + + public void addOutEdge(final Edge e) { + outEdges.computeIfAbsent(e.getSource(), k -> new HashSet<>()).add(e); + } + + private void init() { + outEdges = new HashMap<>(); + + pta.getPag() + .getSimple() + .forEach( + (s, ts) -> { + if (localVarBase(s)) { + ts.forEach( + t -> { + if (localVarBase(t)) { + LocalVarNode toNode = fetchVar(t); + LocalVarNode fromNode = fetchVar(s); + if (fetchLocalVar(s).isInterProcSource()) { + addOutEdge(new Edge(Kind.INTERPROCEDURAL_ASSIGN, fromNode, toNode)); + } else { + if (fetchLocalVar(t).isInterProcTarget()) { + if (!fetchLocalVar(t).isThis()) { + addOutEdge(new Edge(Kind.INTERPROCEDURAL_ASSIGN, fromNode, toNode)); + } + } else { + addOutEdge(new Edge(Kind.LOCAL_ASSIGN, fromNode, toNode)); + } + } + } else if (t instanceof ContextField) { + ContextField ctxField = (ContextField) t; + LocalVarNode varNode = fetchVar(s); + addOutEdge(new Edge(Kind.INSTANCE_STORE, varNode, ctxField)); + } + }); + } else if (s instanceof ContextField) { + ContextField ctxField = (ContextField) s; + ts.forEach( + t -> { + assert localVarBase(t); + LocalVarNode varNode = fetchVar(t); + addOutEdge(new Edge(Kind.INSTANCE_LOAD, ctxField, varNode)); + }); + } + }); + + pta.getCallGraph() + .forEach( + e -> { + Stmt callsite = e.srcStmt(); + SootMethod caller = e.src(); + if (caller != null) { + SootMethod callee = e.tgt(); + if (!callee.isStatic()) { + MethodNodeFactory calleeNF = pta.getPag().getMethodPAG(callee).nodeFactory(); + LocalVarNode thisVar = (LocalVarNode) calleeNF.caseThis(); + AbstractInvokeExpr ie = callsite.getInvokeExpr(); + Value base = null; + if (ie instanceof AbstractInstanceInvokeExpr) { + AbstractInstanceInvokeExpr iie = (AbstractInstanceInvokeExpr) ie; + base = iie.getBase(); + } + if (base != null) { + LocalVarNode fromNode = (LocalVarNode) pta.getPag().findValNode(base, caller); + addOutEdge(new Edge(Kind.INTERPROCEDURAL_ASSIGN, fromNode, thisVar)); + } + } + } else if (Global.isDebug()) { + System.out.println("Null caller of: " + callsite); + } + }); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/mit.txt b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/mit.txt new file mode 100644 index 00000000000..dc47550a578 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/mit.txt @@ -0,0 +1,24 @@ +The MIT License + +Copyright (C) 2018 Yue Li, Tian Tan, Anders Møller, Yannis Smaragdakis + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/sootup.qilin/src/main/java/qilin/pta/tools/BasePTA.java b/sootup.qilin/src/main/java/qilin/pta/tools/BasePTA.java new file mode 100644 index 00000000000..9a482dfd150 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/tools/BasePTA.java @@ -0,0 +1,160 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.tools; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Iterator; +import java.util.Map; +import java.util.TreeMap; +import qilin.CoreConfig; +import qilin.core.CorePTA; +import qilin.core.PTA; +import qilin.core.PTAScene; +import qilin.core.builder.CallGraphBuilder; +import qilin.core.pag.AllocNode; +import qilin.core.pag.ContextVarNode; +import qilin.core.pag.GlobalVarNode; +import qilin.core.pag.LocalVarNode; +import qilin.core.pag.Node; +import qilin.core.pag.PAG; +import qilin.core.pag.ValNode; +import qilin.core.pag.VarNode; +import qilin.core.sets.PointsToSet; +import qilin.core.solver.Propagator; +import qilin.core.solver.Solver; +import qilin.stat.IEvaluator; +import qilin.stat.SimplifiedEvaluator; +import qilin.util.PTAUtils; +import sootup.core.model.SootClass; +import sootup.core.model.SootField; +import sootup.core.model.SootMethod; + +public abstract class BasePTA extends CorePTA { + protected IEvaluator evaluator; + + public BasePTA(PTAScene scene) { + super(scene); + // this.evaluator = new PTAEvaluator(this); + this.evaluator = new SimplifiedEvaluator(this); + } + + public IEvaluator evaluator() { + return this.evaluator; + } + + @Override + protected PAG createPAG() { + return new PAG(this); + } + + @Override + protected CallGraphBuilder createCallGraphBuilder() { + return new CallGraphBuilder(this); + } + + @Override + public Propagator getPropagator() { + return new Solver(this); + } + + @Override + public void run() { + evaluator.begin(); + pureRun(); + evaluator.end(); + dumpStats(); + System.out.println(evaluator()); + } + + protected void dumpStats() { + if (CoreConfig.v().getOutConfig().dumppts) { + dumpPts(this, !CoreConfig.v().getOutConfig().dumplibpts); + } + } + + /** dump pts to sootoutput/pts */ + private void dumpPts(PTA pta, boolean appOnly) { + final String output_dir = CoreConfig.v().getOutConfig().outDir; + Map nodes = new TreeMap<>(); + try { + PrintWriter file = new PrintWriter(new File(output_dir, "pts.txt")); + file.println("Points-to results:"); + for (final ValNode vn : pta.getPag().getValNodes()) { + if (!(vn instanceof VarNode)) { + continue; + } + SootClass clz = null; + if (vn instanceof LocalVarNode) { + SootMethod sm = ((LocalVarNode) vn).getMethod(); + if (sm != null && !PTAUtils.isFakeMainMethod(sm)) { + clz = getView().getClass(sm.getDeclaringClassType()).get(); + } + } else if (vn instanceof GlobalVarNode) { + GlobalVarNode gvn = (GlobalVarNode) vn; + Object variable = gvn.getVariable(); + if (variable instanceof SootField) { + SootField sf = (SootField) variable; + clz = getView().getClass(sf.getDeclaringClassType()).get(); + } + } else if (vn instanceof ContextVarNode) { + ContextVarNode cv = (ContextVarNode) vn; + VarNode varNode = cv.base(); + if (varNode instanceof LocalVarNode) { + LocalVarNode cvbase = (LocalVarNode) varNode; + clz = getView().getClass(cvbase.getMethod().getDeclaringClassType()).get(); + } else if (varNode instanceof GlobalVarNode) { + GlobalVarNode gvn = (GlobalVarNode) varNode; + Object variable = gvn.getVariable(); + if (variable instanceof SootField) { + SootField sf = (SootField) variable; + clz = getView().getClass(sf.getDeclaringClassType()).get(); + } + } + } + if (appOnly && clz != null && !clz.isApplicationClass()) { + continue; + } + + String label = PTAUtils.getNodeLabel(vn); + nodes.put("[" + label + "]", vn); + file.print(label + " -> {"); + PointsToSet p2set = pta.reachingObjects(vn); + + if (p2set == null || p2set.isEmpty()) { + file.print(" empty }\n"); + continue; + } + for (Iterator it = p2set.iterator(); it.hasNext(); ) { + Node n = it.next(); + label = PTAUtils.getNodeLabel(n); + nodes.put("[" + label + "]", n); + file.print(" "); + file.print(label); + } + file.print(" }\n"); + } + nodes.forEach((l, n) -> file.println(l + n)); + file.close(); + } catch (IOException e) { + throw new RuntimeException("Couldn't dump solution." + e); + } + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/tools/BeanPTA.java b/sootup.qilin/src/main/java/qilin/pta/tools/BeanPTA.java new file mode 100644 index 00000000000..38b16b69e1f --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/tools/BeanPTA.java @@ -0,0 +1,72 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.tools; + +import java.util.HashMap; +import java.util.Map; +import qilin.core.PTAScene; +import qilin.parm.ctxcons.CtxConstructor; +import qilin.parm.heapabst.AllocSiteAbstractor; +import qilin.parm.heapabst.HeuristicAbstractor; +import qilin.parm.select.BeanSelector; +import qilin.parm.select.CtxSelector; +import qilin.parm.select.HeuristicSelector; +import qilin.parm.select.PipelineSelector; +import qilin.pta.PTAConfig; +import qilin.pta.toolkits.bean.Bean; +import qilin.util.Stopwatch; + +/* + * refer to "Making k-Object-Sensitive Pointer Analysis More Precise with Still k-Limiting" (SAS'16) + * */ +public class BeanPTA extends StagedPTA { + // currently, we only support k = 2 and hk = 1; + // [current heap, [allocator heap, [heap ctx, new ctx]]] only for B-2obj; + Map>> beanNexCtxMap = new HashMap<>(); + + public BeanPTA(PTAScene scene, CtxConstructor ctxCons) { + super(scene); + this.ctxCons = ctxCons; + CtxSelector us = new BeanSelector(pag, beanNexCtxMap); + if (PTAConfig.v().getPtaConfig().enforceEmptyCtxForIgnoreTypes) { + this.ctxSel = new PipelineSelector(new HeuristicSelector(getView()), us); + } else { + this.ctxSel = us; + } + if (PTAConfig.v().getPtaConfig().mergeHeap) { + this.heapAbst = new HeuristicAbstractor(pag); + } else { + this.heapAbst = new AllocSiteAbstractor(); + } + prePTA = new Spark(scene); + System.out.println("bean ..."); + } + + @Override + protected void preAnalysis() { + Stopwatch sparkTimer = Stopwatch.newAndStart("Spark"); + prePTA.pureRun(); + sparkTimer.stop(); + System.out.println(sparkTimer); + Stopwatch beanTimer = Stopwatch.newAndStart("Bean"); + Bean.run(prePTA, beanNexCtxMap); + beanTimer.stop(); + System.out.println(beanTimer); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/tools/CallSiteSensPTA.java b/sootup.qilin/src/main/java/qilin/pta/tools/CallSiteSensPTA.java new file mode 100644 index 00000000000..31f994f2927 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/tools/CallSiteSensPTA.java @@ -0,0 +1,53 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.tools; + +import qilin.core.PTAScene; +import qilin.parm.ctxcons.CallsiteCtxConstructor; +import qilin.parm.heapabst.AllocSiteAbstractor; +import qilin.parm.heapabst.HeuristicAbstractor; +import qilin.parm.select.CtxSelector; +import qilin.parm.select.HeuristicSelector; +import qilin.parm.select.PipelineSelector; +import qilin.parm.select.UniformSelector; +import qilin.pta.PTAConfig; + +/* + * refer to "Two approaches to interprocedural data flow analysis" (PFA 1981) + * */ + +public class CallSiteSensPTA extends BasePTA { + + public CallSiteSensPTA(PTAScene scene, int k, int hk) { + super(scene); + this.ctxCons = new CallsiteCtxConstructor(); + CtxSelector us = new UniformSelector(k, hk); + if (PTAConfig.v().getPtaConfig().enforceEmptyCtxForIgnoreTypes) { + this.ctxSel = new PipelineSelector(new HeuristicSelector(getView()), us); + } else { + this.ctxSel = us; + } + if (PTAConfig.v().getPtaConfig().mergeHeap) { + this.heapAbst = new HeuristicAbstractor(pag); + } else { + this.heapAbst = new AllocSiteAbstractor(); + } + System.out.println("k-callsite PTA ..."); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/tools/DataDrivenPTA.java b/sootup.qilin/src/main/java/qilin/pta/tools/DataDrivenPTA.java new file mode 100644 index 00000000000..599d1e199ae --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/tools/DataDrivenPTA.java @@ -0,0 +1,59 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.tools; + +import qilin.core.PTAScene; +import qilin.parm.ctxcons.CtxConstructor; +import qilin.parm.heapabst.AllocSiteAbstractor; +import qilin.parm.heapabst.HeuristicAbstractor; +import qilin.parm.select.CtxSelector; +import qilin.parm.select.HeuristicSelector; +import qilin.parm.select.PipelineSelector; +import qilin.pta.PTAConfig; +import qilin.pta.toolkits.dd.DataDrivenSelector; + +/* + * Support Data-driven context-sensitivity for Points-to Analysis (OOPSLA 2017): + * https://doi.org/10.1145/3133924 + * The limitation of this paper is that it only support a flavour of context sensitivity with k = 2. + * k > 2 will costs more time (which is often unbearable) for training. + * The length of heap context is k - 1 by default, where k is a fixed number (i.e., 2) + * We use the same formula presented in the paper. However, our evaluation does not show the same effectiveness + * as claimed in the paper. Maybe we should retrain the formulas in our framework. + * */ + +public class DataDrivenPTA extends BasePTA { + + public DataDrivenPTA(PTAScene scene, CtxConstructor ctxCons) { + super(scene); + this.ctxCons = ctxCons; + CtxSelector us = new DataDrivenSelector(ctxCons.getClass()); + if (PTAConfig.v().getPtaConfig().enforceEmptyCtxForIgnoreTypes) { + this.ctxSel = new PipelineSelector(new HeuristicSelector(getView()), us); + } else { + this.ctxSel = us; + } + if (PTAConfig.v().getPtaConfig().mergeHeap) { + this.heapAbst = new HeuristicAbstractor(pag); + } else { + this.heapAbst = new AllocSiteAbstractor(); + } + System.out.println("data-driven ..."); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/tools/DebloatedPTA.java b/sootup.qilin/src/main/java/qilin/pta/tools/DebloatedPTA.java new file mode 100644 index 00000000000..f3911dde79a --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/tools/DebloatedPTA.java @@ -0,0 +1,204 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.tools; + +import java.util.*; +import qilin.core.context.Context; +import qilin.core.pag.*; +import qilin.core.sets.PointsToSet; +import qilin.core.solver.Propagator; +import qilin.parm.select.CtxSelector; +import qilin.parm.select.DebloatingSelector; +import qilin.parm.select.PipelineSelector; +import qilin.pta.PTAConfig; +import qilin.pta.toolkits.common.DebloatedOAG; +import qilin.pta.toolkits.common.OAG; +import qilin.pta.toolkits.conch.Conch; +import qilin.pta.toolkits.debloaterx.CollectionHeuristic; +import qilin.pta.toolkits.debloaterx.DebloaterX; +import qilin.stat.IEvaluator; +import qilin.util.Stopwatch; +import sootup.core.jimple.basic.Local; +import sootup.core.model.SootField; +import sootup.core.model.SootMethod; + +/* + * refer to "Context Debloating for Object-Sensitive Pointer Analysis" (ASE'21) + * */ +public class DebloatedPTA extends StagedPTA { + public enum DebloatApproach { + CONCH, + DEBLOATERX, + COLLECTION + } + + protected BasePTA basePTA; + protected Set ctxDepHeaps = new HashSet<>(); + protected DebloatApproach debloatApproach; + + /* + * The debloating approach is currently for object-sensitive PTA only. + * Thus the base PTA should be k-OBJ, Zipper-OBJ or Eagle-OBJ. + * */ + /* this constructor is used to specify the debloating approach. */ + public DebloatedPTA(BasePTA basePTA, DebloatApproach approach) { + super(basePTA.getScene()); + this.basePTA = basePTA; + CtxSelector debloatingSelector = new DebloatingSelector(ctxDepHeaps); + basePTA.setContextSelector(new PipelineSelector(basePTA.ctxSelector(), debloatingSelector)); + if (basePTA instanceof StagedPTA) { + StagedPTA stagedPTA = (StagedPTA) basePTA; + this.prePTA = stagedPTA.getPrePTA(); + } else { + this.prePTA = new Spark(basePTA.getScene()); + } + System.out.println("debloating ...."); + this.debloatApproach = approach; + } + + @Override + protected void preAnalysis() { + Stopwatch sparkTimer = Stopwatch.newAndStart("Spark"); + prePTA.pureRun(); + sparkTimer.stop(); + System.out.println(sparkTimer); + if (debloatApproach == DebloatApproach.CONCH) { + Stopwatch conchTimer = Stopwatch.newAndStart("Conch"); + Conch hc = new Conch(prePTA); + hc.runClassifier(); + this.ctxDepHeaps.addAll(hc.ctxDependentHeaps()); + System.out.println(); + conchTimer.stop(); + System.out.println(conchTimer); + } else if (debloatApproach == DebloatApproach.DEBLOATERX) { + Stopwatch debloaterXTimer = Stopwatch.newAndStart("DebloaterX"); + DebloaterX debloaterX = new DebloaterX(prePTA); + debloaterX.run(); + Set mCtxDepHeaps = debloaterX.getCtxDepHeaps(); + for (AllocNode obj : mCtxDepHeaps) { + this.ctxDepHeaps.add(obj.getNewExpr()); + } + System.out.println(); + debloaterXTimer.stop(); + System.out.println(debloaterXTimer); + // stat OAG reductions + OAG oag = new OAG(prePTA); + oag.build(); + OAG doag1 = new DebloatedOAG(prePTA, mCtxDepHeaps); + doag1.build(); + System.out.println("OAG #node:" + oag.nodeSize() + "; #edge:" + oag.edgeSize()); + System.out.println( + "DebloaterX OAG #node:" + doag1.nodeSize() + "; #edge:" + doag1.edgeSize()); + } else { + assert (debloatApproach == DebloatApproach.COLLECTION); + Stopwatch collectionHeuristic = Stopwatch.newAndStart("COLLECTION"); + CollectionHeuristic ch = new CollectionHeuristic(prePTA); + ch.run(); + collectionHeuristic.stop(); + System.out.println(collectionHeuristic); + for (AllocNode obj : ch.getCtxDepHeaps()) { + this.ctxDepHeaps.add(obj.getNewExpr()); + } + } + } + + @Override + protected void mainAnalysis() { + if (!PTAConfig.v().getPtaConfig().preAnalysisOnly) { + System.out.println("selective pta starts!"); + basePTA.run(); + } + } + + @Override + public Propagator getPropagator() { + return basePTA.getPropagator(); + } + + @Override + public Node parameterize(Node n, Context context) { + return basePTA.parameterize(n, context); + } + + @Override + public ContextMethod parameterize(SootMethod method, Context context) { + return basePTA.parameterize(method, context); + } + + @Override + public AllocNode getRootNode() { + return basePTA.getRootNode(); + } + + @Override + public IEvaluator evaluator() { + return basePTA.evaluator(); + } + + @Override + public Context emptyContext() { + return basePTA.emptyContext(); + } + + @Override + public Context createCalleeCtx( + ContextMethod caller, AllocNode receiverNode, CallSite callSite, SootMethod target) { + return basePTA.createCalleeCtx(caller, receiverNode, callSite, target); + } + + @Override + public PointsToSet reachingObjects(SootMethod m, Local l) { + return basePTA.reachingObjects(m, l); + } + + @Override + public PointsToSet reachingObjects(Node n) { + return basePTA.reachingObjects(n); + } + + @Override + public PointsToSet reachingObjects(Context c, SootMethod m, Local l) { + return basePTA.reachingObjects(c, m, l); + } + + @Override + public PointsToSet reachingObjects(SootField f) { + return basePTA.reachingObjects(f); + } + + @Override + public PointsToSet reachingObjects(PointsToSet s, SootField f) { + return basePTA.reachingObjects(s, f); + } + + @Override + public PointsToSet reachingObjects(SootMethod m, Local l, SootField f) { + return basePTA.reachingObjects(m, l, f); + } + + @Override + public PointsToSet reachingObjects(Context c, SootMethod m, Local l, SootField f) { + return basePTA.reachingObjects(c, m, l, f); + } + + @Override + public PointsToSet reachingObjectsOfArrayElement(PointsToSet s) { + return basePTA.reachingObjectsOfArrayElement(s); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/tools/EaglePTA.java b/sootup.qilin/src/main/java/qilin/pta/tools/EaglePTA.java new file mode 100644 index 00000000000..0fbb368c56e --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/tools/EaglePTA.java @@ -0,0 +1,128 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.tools; + +import java.io.FileNotFoundException; +import java.io.PrintWriter; +import java.util.Map; +import qilin.core.PTAScene; +import qilin.core.pag.AllocNode; +import qilin.core.pag.LocalVarNode; +import qilin.core.pag.Parm; +import qilin.pta.toolkits.eagle.Eagle; +import qilin.util.Pair; +import qilin.util.Stopwatch; +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.common.expr.Expr; +import sootup.core.jimple.common.expr.JNewArrayExpr; +import sootup.core.jimple.common.stmt.Stmt; + +/* + * refer to "Precision-Preserving Yet Fast Object-Sensitive Pointer Analysis with Partial Context Sensitivity" (OOPSLA'19) + * and "Eagle: CFL-Reachability-based Precision-Preserving Acceleration of Object-Sensitive Pointer Analysis with Partial Context Sensitivity" + * (TOSEM'21) + * */ +public class EaglePTA extends PartialObjSensPTA { + public EaglePTA(PTAScene scene, int ctxLen) { + super(scene, ctxLen); + System.out.println("Eagle ...."); + } + + @Override + protected Map calculatingNode2Length() { + Stopwatch timer = Stopwatch.newAndStart("TransGraph Construction"); + Eagle eagle = new Eagle(); + eagle.buildGraph(prePTA); + timer.stop(); + System.out.println(timer); + Stopwatch eagleTimer = Stopwatch.newAndStart("Eagle Selection"); + Map ret = eagle.contxtLengthAnalysis(); + eagleTimer.stop(); + System.out.println(eagleTimer); + eagle.dumpCount(); + // try { + // writeToFile(ret); + // } catch (FileNotFoundException e) { + // System.out.println("no file exists for dumping eagle selected partial + // heaps/variables."); + // } + System.out.println("#Node:" + eagle.totalNodesCount()); + System.out.println("#Edge:" + eagle.totalEdgesCount()); + return ret; + } + + protected void writeToFile(Map partialResults) throws FileNotFoundException { + final char EOL = '\n'; + String insensVarFile = "InsensitiveVar.facts"; + String insensHeapFile = "InsensitiveHeap.facts"; + PrintWriter varWriter = new PrintWriter(insensVarFile); + PrintWriter heapWriter = new PrintWriter(insensHeapFile); + partialResults.forEach( + (k, v) -> { + if (v > 0) { + return; + } + if (k instanceof AllocNode) { + AllocNode heap = (AllocNode) k; + if (heap.getMethod() == null) { + return; + } + String newExpr = heap.getNewExpr().toString(); + if (heap.getNewExpr() instanceof JNewArrayExpr) { + JNewArrayExpr nae = (JNewArrayExpr) heap.getNewExpr(); + newExpr = "new " + nae.getBaseType() + "[]"; + } + String heapSig = heap.getMethod().toString() + "/" + newExpr; + heapWriter.write(heapSig); + heapWriter.write(EOL); + } else if (k instanceof LocalVarNode) { + LocalVarNode lvn = (LocalVarNode) k; + Object variable = lvn.getVariable(); + String varName = variable.toString(); + if (variable instanceof Parm) { + Parm parm = (Parm) variable; + if (parm.isThis()) { + varName = "@this"; + } else if (parm.isReturn()) { + return; + } else if (parm.isThrowRet()) { + return; + } else { + varName = "@parameter" + parm.getIndex(); + } + } else if (variable instanceof Local) { + + } else if (variable instanceof Stmt) { + return; + } else if (variable instanceof Expr) { + return; + } else if (variable instanceof Pair) { + return; + } else { + return; + } + String varSig = lvn.getMethod().toString() + "/" + varName; + varWriter.write(varSig); + varWriter.write(EOL); + } + }); + varWriter.close(); + heapWriter.close(); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/tools/HybridObjectSensPTA.java b/sootup.qilin/src/main/java/qilin/pta/tools/HybridObjectSensPTA.java new file mode 100644 index 00000000000..619857886fc --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/tools/HybridObjectSensPTA.java @@ -0,0 +1,50 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.tools; + +import qilin.core.PTAScene; +import qilin.parm.ctxcons.HybObjCtxConstructor; +import qilin.parm.heapabst.AllocSiteAbstractor; +import qilin.parm.heapabst.HeuristicAbstractor; +import qilin.parm.select.CtxSelector; +import qilin.parm.select.HeuristicSelector; +import qilin.parm.select.PipelineSelector; +import qilin.parm.select.UniformSelector; +import qilin.pta.PTAConfig; + +/** refer to "Hybrid Context-Sensitivity for Points-To Analysis" (PLDI'13) */ +public class HybridObjectSensPTA extends BasePTA { + + public HybridObjectSensPTA(PTAScene scene, int k, int hk) { + super(scene); + this.ctxCons = new HybObjCtxConstructor(); + CtxSelector us = new UniformSelector(k + 1, hk); + if (PTAConfig.v().getPtaConfig().enforceEmptyCtxForIgnoreTypes) { + this.ctxSel = new PipelineSelector(new HeuristicSelector(getView()), us); + } else { + this.ctxSel = us; + } + if (PTAConfig.v().getPtaConfig().mergeHeap) { + this.heapAbst = new HeuristicAbstractor(pag); + } else { + this.heapAbst = new AllocSiteAbstractor(); + } + System.out.println("Hybrid k-OBJ ..."); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/tools/HybridTypeSensPTA.java b/sootup.qilin/src/main/java/qilin/pta/tools/HybridTypeSensPTA.java new file mode 100644 index 00000000000..533b3429a44 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/tools/HybridTypeSensPTA.java @@ -0,0 +1,50 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.tools; + +import qilin.core.PTAScene; +import qilin.parm.ctxcons.HybTypeCtxConstructor; +import qilin.parm.heapabst.AllocSiteAbstractor; +import qilin.parm.heapabst.HeuristicAbstractor; +import qilin.parm.select.CtxSelector; +import qilin.parm.select.HeuristicSelector; +import qilin.parm.select.PipelineSelector; +import qilin.parm.select.UniformSelector; +import qilin.pta.PTAConfig; + +/** refer to "Hybrid Context-Sensitivity for Points-To Analysis" (PLDI'13) */ +public class HybridTypeSensPTA extends BasePTA { + + public HybridTypeSensPTA(PTAScene scene, int k, int hk) { + super(scene); + this.ctxCons = new HybTypeCtxConstructor(); + CtxSelector us = new UniformSelector(k, hk); + if (PTAConfig.v().getPtaConfig().enforceEmptyCtxForIgnoreTypes) { + this.ctxSel = new PipelineSelector(new HeuristicSelector(getView()), us); + } else { + this.ctxSel = us; + } + if (PTAConfig.v().getPtaConfig().mergeHeap) { + this.heapAbst = new HeuristicAbstractor(pag); + } else { + this.heapAbst = new AllocSiteAbstractor(); + } + System.out.println("Hybrid k-type ..."); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/tools/MahjongPTA.java b/sootup.qilin/src/main/java/qilin/pta/tools/MahjongPTA.java new file mode 100644 index 00000000000..6d55485de2f --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/tools/MahjongPTA.java @@ -0,0 +1,77 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.tools; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import qilin.core.PTAScene; +import qilin.core.pag.PAG; +import qilin.parm.ctxcons.CtxConstructor; +import qilin.parm.heapabst.MahjongAbstractor; +import qilin.parm.select.CtxSelector; +import qilin.parm.select.DebloatingSelector; +import qilin.parm.select.PipelineSelector; +import qilin.parm.select.UniformSelector; +import qilin.pta.PTAConfig; +import qilin.pta.toolkits.mahjong.Mahjong; + +/* + * refer to "Efficient and Precise Points-to Analysis: Modeling the Heap by Merging Equivalent Automata" (PLDI'17) + * */ +public class MahjongPTA extends StagedPTA { + protected final Map heapModelMap = new HashMap<>(); + public Set mergedHeap = new HashSet<>(); + public Set csHeap = new HashSet<>(); + + public MahjongPTA(PTAScene scene, int k, int hk, CtxConstructor ctxCons) { + super(scene); + this.ctxCons = ctxCons; + CtxSelector us = new UniformSelector(k, hk); + CtxSelector ds = new DebloatingSelector(csHeap); + this.ctxSel = new PipelineSelector(us, ds); + this.heapAbst = new MahjongAbstractor(pag, mergedHeap, heapModelMap); + this.prePTA = new Spark(scene); + System.out.println("Mahjong ..."); + } + + @Override + protected void preAnalysis() { + PTAConfig.v().getPtaConfig().mergeHeap = false; + prePTA.pureRun(); + PAG prePAG = prePTA.getPag(); + Mahjong.run(prePTA, heapModelMap); + heapModelMap.forEach( + (origin, merged) -> { + if (prePAG.findAllocNode(origin) == null || prePAG.findAllocNode(merged) == null) { + return; + } + if (prePAG.findAllocNode(origin).getType() != prePAG.findAllocNode(merged).getType()) { + return; + } + if (!csHeap.add(merged)) { + mergedHeap.add(merged); + } + }); + csHeap.removeAll(mergedHeap); + System.out.println("#MERGE HEAP (not-single):" + mergedHeap.size()); + System.out.println("#NON-MERGED HEAP (single):" + csHeap.size()); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/tools/ObjectSensPTA.java b/sootup.qilin/src/main/java/qilin/pta/tools/ObjectSensPTA.java new file mode 100644 index 00000000000..1509c25e70c --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/tools/ObjectSensPTA.java @@ -0,0 +1,52 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.tools; + +import qilin.core.PTAScene; +import qilin.parm.ctxcons.ObjCtxConstructor; +import qilin.parm.heapabst.AllocSiteAbstractor; +import qilin.parm.heapabst.HeuristicAbstractor; +import qilin.parm.select.CtxSelector; +import qilin.parm.select.HeuristicSelector; +import qilin.parm.select.PipelineSelector; +import qilin.parm.select.UniformSelector; +import qilin.pta.PTAConfig; + +/* + * refer to "Parameterized object sensitivity for points-to analysis for Java" (TSE'05) + * */ +public class ObjectSensPTA extends BasePTA { + public ObjectSensPTA(PTAScene scene, int k, int hk) { + super(scene); + this.ctxCons = new ObjCtxConstructor(); + CtxSelector us = new UniformSelector(k, hk); + if (PTAConfig.v().getPtaConfig().enforceEmptyCtxForIgnoreTypes) { + this.ctxSel = new PipelineSelector(new HeuristicSelector(getView()), us); + } else { + this.ctxSel = us; + } + if (PTAConfig.v().getPtaConfig().mergeHeap) { + System.out.println(".... Heuristic..."); + this.heapAbst = new HeuristicAbstractor(pag); + } else { + this.heapAbst = new AllocSiteAbstractor(); + } + System.out.println("k-OBJ ..."); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/tools/PartialCallSiteSensPTA.java b/sootup.qilin/src/main/java/qilin/pta/tools/PartialCallSiteSensPTA.java new file mode 100644 index 00000000000..8e23047f4bf --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/tools/PartialCallSiteSensPTA.java @@ -0,0 +1,197 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.tools; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import qilin.core.PTAScene; +import qilin.core.pag.*; +import qilin.parm.ctxcons.CallsiteCtxConstructor; +import qilin.parm.heapabst.AllocSiteAbstractor; +import qilin.parm.heapabst.HeuristicAbstractor; +import qilin.parm.select.*; +import qilin.pta.PTAConfig; +import qilin.util.PTAUtils; +import qilin.util.Stopwatch; +import qilin.util.queue.QueueReader; +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.basic.Value; +import sootup.core.jimple.common.constant.NullConstant; +import sootup.core.jimple.common.expr.AbstractInstanceInvokeExpr; +import sootup.core.jimple.common.expr.AbstractInvokeExpr; +import sootup.core.jimple.common.stmt.JAssignStmt; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.SootMethod; +import sootup.core.types.ReferenceType; + +public abstract class PartialCallSiteSensPTA extends StagedPTA { + protected Set csnodes = new HashSet<>(); + protected Set csmethods = new HashSet<>(); + + // just for stats + Set PCSM = new HashSet<>(); + Set CSM = new HashSet<>(); + + public PartialCallSiteSensPTA(PTAScene scene, int ctxLen) { + super(scene); + this.ctxCons = new CallsiteCtxConstructor(); + CtxSelector us = new PartialVarSelector(ctxLen, ctxLen - 1, csnodes, csmethods); + if (PTAConfig.v().getPtaConfig().enforceEmptyCtxForIgnoreTypes) { + this.ctxSel = new PipelineSelector(new HeuristicSelector(getView()), us); + } else { + this.ctxSel = us; + } + if (PTAConfig.v().getPtaConfig().mergeHeap) { + this.heapAbst = new HeuristicAbstractor(pag); + } else { + this.heapAbst = new AllocSiteAbstractor(); + } + this.prePTA = new Spark(scene); + } + + @Override + protected void preAnalysis() { + Stopwatch sparkTimer = Stopwatch.newAndStart("Spark"); + prePTA.pureRun(); + sparkTimer.stop(); + System.out.println(sparkTimer); + select(); + extraStats(); + } + + protected abstract Map calculatingNode2Length(); + + // =========context selector============= + protected void select() { + Stopwatch preTimer = Stopwatch.newAndStart("pre-analysis"); + Map ret = calculatingNode2Length(); + ret.forEach( + (sparkNode, l) -> { + if (l > 0) { + csnodes.add(PTAUtils.getIR(sparkNode)); + } + SootMethod method = null; + if (sparkNode instanceof LocalVarNode) { + method = ((LocalVarNode) sparkNode).getMethod(); + } else if (sparkNode instanceof AllocNode) { + AllocNode allocNode = (AllocNode) sparkNode; + method = allocNode.getMethod(); + } + + if (method != null) { + if (l == 0) { + PCSM.add(method); + } else { + CSM.add(method); + } + } + }); + PCSM.retainAll(CSM); + CSM.removeAll(PCSM); + csmethods.addAll(CSM); + csmethods.addAll(PCSM); + System.out.println("#CSNODES:" + csnodes.size()); + System.out.println("#CSMETHODS:" + csmethods.size()); + preTimer.stop(); + System.out.println(preTimer); + } + + protected void extraStats() { + int[] RM = new int[1], PCN = new int[1], NPCN = new int[1], totalN = new int[1]; + for (ContextMethod momc : prePTA.getReachableMethods()) { + SootMethod method = momc.method(); + Set nodes = new HashSet<>(); + if (!PTAUtils.hasBody(method)) { + return; + } + PAG prePAG = prePTA.getPag(); + MethodPAG srcmpag = prePAG.getMethodPAG(method); + QueueReader reader = srcmpag.getInternalReader().clone(); + while (reader.hasNext()) { + Node from = reader.next(), to = reader.next(); + if (from instanceof LocalVarNode) { + nodes.add(((VarNode) from).getVariable()); + } else if (from instanceof AllocNode) { + nodes.add(((AllocNode) from).getNewExpr()); + } else if (from instanceof FieldRefNode) { + FieldRefNode fr = (FieldRefNode) from; + VarNode base = fr.getBase(); + if (base instanceof LocalVarNode) { + nodes.add(base.getVariable()); + } + } + + if (to instanceof LocalVarNode) { + nodes.add(((VarNode) to).getVariable()); + } else if (to instanceof FieldRefNode) { + FieldRefNode fr = (FieldRefNode) to; + VarNode base = fr.getBase(); + if (base instanceof LocalVarNode) { + nodes.add(base.getVariable()); + } + } + } + for (final Stmt s : srcmpag.getInvokeStmts()) { + AbstractInvokeExpr ie = s.getInvokeExpr(); + int numArgs = ie.getArgCount(); + for (int i = 0; i < numArgs; i++) { + Value arg = ie.getArg(i); + if (!(arg.getType() instanceof ReferenceType) || arg instanceof NullConstant) { + continue; + } + nodes.add(arg); + } + + if (s instanceof JAssignStmt) { + Value dest = ((JAssignStmt) s).getLeftOp(); + if (dest.getType() instanceof ReferenceType) { + nodes.add(dest); + } + } + if (ie instanceof AbstractInstanceInvokeExpr) { + AbstractInstanceInvokeExpr iie = (AbstractInstanceInvokeExpr) ie; + Local base = iie.getBase(); + nodes.add(base); + } + } + for (Object o : nodes) { + int index = 0; + if (csnodes.contains(o)) { + index = 1; + } + if (index != 0) { + PCN[0]++; + } else { + NPCN[0]++; + } + totalN[0]++; + } + RM[0]++; + } + RM[0]--; // For the FakeMain method. + System.out.println("#ReachableMethod:" + RM[0]); + System.out.println("#FCSM:" + CSM.size()); + System.out.println("#PCSM:" + PCSM.size()); + System.out.println("#CIM:" + (RM[0] - PCSM.size() - CSM.size())); + System.out.println("#CIN: " + NPCN[0]); + System.out.println("#CSN: " + PCN[0]); + System.out.println("totalN: " + totalN[0]); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/tools/PartialObjSensPTA.java b/sootup.qilin/src/main/java/qilin/pta/tools/PartialObjSensPTA.java new file mode 100644 index 00000000000..20029388eb6 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/tools/PartialObjSensPTA.java @@ -0,0 +1,202 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.tools; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import qilin.core.PTAScene; +import qilin.core.pag.*; +import qilin.parm.ctxcons.ObjCtxConstructor; +import qilin.parm.heapabst.AllocSiteAbstractor; +import qilin.parm.heapabst.HeuristicAbstractor; +import qilin.parm.select.CtxSelector; +import qilin.parm.select.HeuristicSelector; +import qilin.parm.select.PartialVarSelector; +import qilin.parm.select.PipelineSelector; +import qilin.pta.PTAConfig; +import qilin.util.PTAUtils; +import qilin.util.Stopwatch; +import qilin.util.queue.QueueReader; +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.basic.Value; +import sootup.core.jimple.common.constant.NullConstant; +import sootup.core.jimple.common.expr.AbstractInstanceInvokeExpr; +import sootup.core.jimple.common.expr.AbstractInvokeExpr; +import sootup.core.jimple.common.stmt.JAssignStmt; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.SootMethod; +import sootup.core.types.ReferenceType; + +public abstract class PartialObjSensPTA extends StagedPTA { + protected Set csnodes = new HashSet<>(); + protected Set csmethods = new HashSet<>(); + protected PAG prePAG; + + // just for stats + Set PCSM = new HashSet<>(); + Set CSM = new HashSet<>(); + + public PartialObjSensPTA(PTAScene scene, int ctxLen) { + super(scene); + this.ctxCons = new ObjCtxConstructor(); + CtxSelector us = new PartialVarSelector(ctxLen, ctxLen - 1, csnodes, csmethods); + if (PTAConfig.v().getPtaConfig().enforceEmptyCtxForIgnoreTypes) { + this.ctxSel = new PipelineSelector(new HeuristicSelector(getView()), us); + } else { + this.ctxSel = us; + } + if (PTAConfig.v().getPtaConfig().mergeHeap) { + this.heapAbst = new HeuristicAbstractor(pag); + } else { + this.heapAbst = new AllocSiteAbstractor(); + } + this.prePTA = new Spark(scene); + this.prePAG = prePTA.getPag(); + } + + @Override + protected void preAnalysis() { + Stopwatch sparkTimer = Stopwatch.newAndStart("Spark"); + prePTA.pureRun(); + sparkTimer.stop(); + System.out.println(sparkTimer); + select(); + extraStats(); + } + + protected abstract Map calculatingNode2Length(); + + // =========context selector============= + protected void select() { + Stopwatch preTimer = Stopwatch.newAndStart("pre-analysis"); + Map ret = calculatingNode2Length(); + ret.forEach( + (sparkNode, l) -> { + if (l > 0) { + csnodes.add(PTAUtils.getIR(sparkNode)); + } + SootMethod method = null; + if (sparkNode instanceof LocalVarNode) { + method = ((LocalVarNode) sparkNode).getMethod(); + } else if (sparkNode instanceof AllocNode) { + AllocNode allocNode = (AllocNode) sparkNode; + method = allocNode.getMethod(); + } + + if (method != null) { + if (l == 0) { + PCSM.add(method); + } else { + CSM.add(method); + } + } + }); + PCSM.retainAll(CSM); + CSM.removeAll(PCSM); + csmethods.addAll(CSM); + csmethods.addAll(PCSM); + System.out.println("#CSNODES:" + csnodes.size()); + System.out.println("#CSMETHODS:" + csmethods.size()); + preTimer.stop(); + System.out.println(preTimer); + } + + protected void extraStats() { + int[] RM = new int[1], PCN = new int[1], NPCN = new int[1], totalN = new int[1]; + for (ContextMethod momc : prePTA.getReachableMethods()) { + SootMethod method = momc.method(); + Set nodes = new HashSet<>(); + if (!PTAUtils.hasBody(method)) { + return; + } + MethodPAG srcmpag = pag.getMethodPAG(method); + QueueReader reader = srcmpag.getInternalReader().clone(); + while (reader.hasNext()) { + Node from = reader.next(), to = reader.next(); + if (from instanceof LocalVarNode) { + nodes.add(((VarNode) from).getVariable()); + } else if (from instanceof AllocNode) { + nodes.add(((AllocNode) from).getNewExpr()); + } else if (from instanceof FieldRefNode) { + FieldRefNode fr = (FieldRefNode) from; + VarNode base = fr.getBase(); + if (base instanceof LocalVarNode) { + nodes.add(base.getVariable()); + } + } + + if (to instanceof LocalVarNode) { + nodes.add(((VarNode) to).getVariable()); + } else if (to instanceof FieldRefNode) { + FieldRefNode fr = (FieldRefNode) to; + VarNode base = fr.getBase(); + if (base instanceof LocalVarNode) { + nodes.add(base.getVariable()); + } + } + } + for (final Stmt s : srcmpag.getInvokeStmts()) { + AbstractInvokeExpr ie = s.getInvokeExpr(); + int numArgs = ie.getArgCount(); + for (int i = 0; i < numArgs; i++) { + Value arg = ie.getArg(i); + if (!(arg.getType() instanceof ReferenceType) || arg instanceof NullConstant) { + continue; + } + nodes.add(arg); + } + + if (s instanceof JAssignStmt) { + Value dest = ((JAssignStmt) s).getLeftOp(); + if (dest.getType() instanceof ReferenceType) { + nodes.add(dest); + } + } + if (ie instanceof AbstractInstanceInvokeExpr) { + AbstractInstanceInvokeExpr iie = (AbstractInstanceInvokeExpr) ie; + Local base = iie.getBase(); + nodes.add(base); + } + } + for (Object o : nodes) { + int index = 0; + if (csnodes.contains(o)) { + index = 1; + } + if (index != 0) { + PCN[0]++; + } else { + NPCN[0]++; + } + totalN[0]++; + } + // System.out.println(method); + RM[0]++; + } + RM[0]--; // For the FakeMain method. + System.out.println("#ReachableMethod:" + RM[0]); + System.out.println("#FCSM:" + CSM.size()); + System.out.println("#PCSM:" + PCSM.size()); + System.out.println("#CIM:" + (RM[0] - PCSM.size() - CSM.size())); + System.out.println("#CIN: " + NPCN[0]); + System.out.println("#CSN: " + PCN[0]); + System.out.println("totalN: " + totalN[0]); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/tools/SelectxPTA.java b/sootup.qilin/src/main/java/qilin/pta/tools/SelectxPTA.java new file mode 100644 index 00000000000..b866fa2d388 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/tools/SelectxPTA.java @@ -0,0 +1,46 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.tools; + +import java.util.Map; +import qilin.core.PTAScene; +import qilin.pta.toolkits.selectx.Selectx; + +public class SelectxPTA extends PartialCallSiteSensPTA { + + public SelectxPTA(PTAScene scene, int ctxLen) { + super(scene, ctxLen); + System.out.println("selectx ... "); + } + + // =========context selector============= + + @Override + protected Map calculatingNode2Length() { + System.out.print("Construct transPAG..."); + long time = System.currentTimeMillis(); + + Selectx selectx = new Selectx(prePTA); + + System.out.println((System.currentTimeMillis() - time) / 1000 + "s"); + + System.out.println("Propagate.."); + return selectx.process(); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/tools/Spark.java b/sootup.qilin/src/main/java/qilin/pta/tools/Spark.java new file mode 100644 index 00000000000..457e45a0f25 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/tools/Spark.java @@ -0,0 +1,43 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.tools; + +import qilin.core.PTAScene; +import qilin.parm.ctxcons.InsensCtxConstructor; +import qilin.parm.heapabst.AllocSiteAbstractor; +import qilin.parm.heapabst.HeuristicAbstractor; +import qilin.parm.select.InsenSelector; +import qilin.pta.PTAConfig; + +/* + * refer to "Scaling Java Points-To Analysis using SPARK" (CC'03) + * */ +public class Spark extends BasePTA { + public Spark(PTAScene scene) { + super(scene); + this.ctxCons = new InsensCtxConstructor(); + this.ctxSel = new InsenSelector(); + if (PTAConfig.v().getPtaConfig().mergeHeap) { + this.heapAbst = new HeuristicAbstractor(pag); + } else { + this.heapAbst = new AllocSiteAbstractor(); + } + System.out.println("Context-Insensitive ..."); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/tools/StagedPTA.java b/sootup.qilin/src/main/java/qilin/pta/tools/StagedPTA.java new file mode 100644 index 00000000000..f20a8e37e8f --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/tools/StagedPTA.java @@ -0,0 +1,54 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.tools; + +import qilin.core.PTAScene; +import qilin.pta.PTAConfig; + +/* + * Many recent pointer analyses are two-staged analyses with a preanalysis and a main analysis. + * This class gives a structure for such kinds of analyses. + * */ +public abstract class StagedPTA extends BasePTA { + protected BasePTA prePTA; + + public StagedPTA(PTAScene scene) { + super(scene); + } + + public BasePTA getPrePTA() { + return this.prePTA; + } + + protected abstract void preAnalysis(); + + protected void mainAnalysis() { + if (!PTAConfig.v().getPtaConfig().preAnalysisOnly) { + System.out.println("selective pta starts!"); + super.run(); + } + } + + @Override + public void run() { + preAnalysis(); + prePTA.getPag().resetPointsToSet(); + mainAnalysis(); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/tools/TunnelingPTA.java b/sootup.qilin/src/main/java/qilin/pta/tools/TunnelingPTA.java new file mode 100644 index 00000000000..5869afeebd4 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/tools/TunnelingPTA.java @@ -0,0 +1,54 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.tools; + +import qilin.core.PTAScene; +import qilin.parm.ctxcons.CtxConstructor; +import qilin.parm.heapabst.AllocSiteAbstractor; +import qilin.parm.heapabst.HeuristicAbstractor; +import qilin.parm.select.CtxSelector; +import qilin.parm.select.HeuristicSelector; +import qilin.parm.select.PipelineSelector; +import qilin.parm.select.UniformSelector; +import qilin.pta.PTAConfig; +import qilin.pta.toolkits.dd.TunnelingConstructor; + +/* + * This class support context tunneling from the paper "Precise and Scalable Points-to Analysis via Data-Driven + * Context Tunneling" (OOPSLA 2018). We reuse the trained formula from the paper. However, our evaluation does + * not show the claimed effectiveness. Maybe we should train the benchmarks to get new formulas? + * */ +public class TunnelingPTA extends BasePTA { + public TunnelingPTA(PTAScene scene, CtxConstructor ctxCons, int k, int hk) { + super(scene); + this.ctxCons = new TunnelingConstructor(getView(), ctxCons); + CtxSelector us = new UniformSelector(k, hk); + if (PTAConfig.v().getPtaConfig().enforceEmptyCtxForIgnoreTypes) { + this.ctxSel = new PipelineSelector(new HeuristicSelector(getView()), us); + } else { + this.ctxSel = us; + } + if (PTAConfig.v().getPtaConfig().mergeHeap) { + this.heapAbst = new HeuristicAbstractor(pag); + } else { + this.heapAbst = new AllocSiteAbstractor(); + } + System.out.println("context-tunneling ..."); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/tools/TurnerPTA.java b/sootup.qilin/src/main/java/qilin/pta/tools/TurnerPTA.java new file mode 100644 index 00000000000..d5d1686d8df --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/tools/TurnerPTA.java @@ -0,0 +1,42 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.tools; + +import java.util.Map; +import qilin.core.PTAScene; +import qilin.pta.toolkits.turner.Turner; + +/* + * refer to "Accelerating Object-Sensitive Pointer Analysis by Exploiting Object Containment and Reachability" (ECOOP'21) + * */ +public class TurnerPTA extends PartialObjSensPTA { + private final int k; + + public TurnerPTA(PTAScene scene, int k) { + super(scene, k); + this.k = k; + System.out.println("Turner ..."); + } + + @Override + protected Map calculatingNode2Length() { + Turner turner = new Turner(k, prePTA); + return turner.contxtLengthAnalysis(); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/tools/TypeSensPTA.java b/sootup.qilin/src/main/java/qilin/pta/tools/TypeSensPTA.java new file mode 100644 index 00000000000..ba3773f01c3 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/tools/TypeSensPTA.java @@ -0,0 +1,52 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.tools; + +import qilin.core.PTAScene; +import qilin.parm.ctxcons.TypeCtxConstructor; +import qilin.parm.heapabst.AllocSiteAbstractor; +import qilin.parm.heapabst.HeuristicAbstractor; +import qilin.parm.select.CtxSelector; +import qilin.parm.select.HeuristicSelector; +import qilin.parm.select.PipelineSelector; +import qilin.parm.select.UniformSelector; +import qilin.pta.PTAConfig; + +/* + * refer to "Pick Your Contexts Well: Understanding Object-Sensitivity" (PLDI'11) + * */ +public class TypeSensPTA extends BasePTA { + + public TypeSensPTA(PTAScene scene, int k, int hk) { + super(scene); + this.ctxCons = new TypeCtxConstructor(); + CtxSelector us = new UniformSelector(k, hk); + if (PTAConfig.v().getPtaConfig().enforceEmptyCtxForIgnoreTypes) { + this.ctxSel = new PipelineSelector(new HeuristicSelector(getView()), us); + } else { + this.ctxSel = us; + } + if (PTAConfig.v().getPtaConfig().mergeHeap) { + this.heapAbst = new HeuristicAbstractor(pag); + } else { + this.heapAbst = new AllocSiteAbstractor(); + } + System.out.println("k-type ..."); + } +} diff --git a/sootup.qilin/src/main/java/qilin/pta/tools/ZipperPTA.java b/sootup.qilin/src/main/java/qilin/pta/tools/ZipperPTA.java new file mode 100644 index 00000000000..a239b1ed997 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/pta/tools/ZipperPTA.java @@ -0,0 +1,166 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.tools; + +import java.util.HashSet; +import java.util.Set; +import qilin.core.PTAScene; +import qilin.core.pag.*; +import qilin.parm.ctxcons.CtxConstructor; +import qilin.parm.heapabst.AllocSiteAbstractor; +import qilin.parm.heapabst.HeuristicAbstractor; +import qilin.parm.select.CtxSelector; +import qilin.parm.select.HeuristicSelector; +import qilin.parm.select.PartialMethodLvSelector; +import qilin.parm.select.PipelineSelector; +import qilin.pta.PTAConfig; +import qilin.pta.toolkits.zipper.Main; +import qilin.util.PTAUtils; +import qilin.util.Stopwatch; +import qilin.util.queue.QueueReader; +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.basic.Value; +import sootup.core.jimple.common.constant.NullConstant; +import sootup.core.jimple.common.expr.AbstractInstanceInvokeExpr; +import sootup.core.jimple.common.expr.AbstractInvokeExpr; +import sootup.core.jimple.common.stmt.JAssignStmt; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.SootMethod; +import sootup.core.types.ReferenceType; + +/* + * refer to "Precision-Guided Context Sensitivity for Pointer Analysis" (OOPSLA'18) + * and "A Principled Approach to Selective Context Sensitivity for Pointer Analysis" (TOPLAS'20) + * */ +public class ZipperPTA extends StagedPTA { + private final Set PCMs = new HashSet<>(); + + /* + * Zipper support object-sensitivity, callsite-sensitivity by using corresponding + * context-constructor. + * */ + public ZipperPTA(PTAScene scene, int k, int hk, CtxConstructor ctxCons) { + super(scene); + this.ctxCons = ctxCons; + CtxSelector us = new PartialMethodLvSelector(k, hk, PCMs); + if (PTAConfig.v().getPtaConfig().enforceEmptyCtxForIgnoreTypes) { + this.ctxSel = new PipelineSelector(new HeuristicSelector(getView()), us); + } else { + this.ctxSel = us; + } + if (PTAConfig.v().getPtaConfig().mergeHeap) { + this.heapAbst = new HeuristicAbstractor(pag); + } else { + this.heapAbst = new AllocSiteAbstractor(); + } + this.prePTA = new Spark(scene); + } + + @Override + protected void preAnalysis() { + Stopwatch sparkTimer = Stopwatch.newAndStart("Spark"); + prePTA.pureRun(); + sparkTimer.stop(); + System.out.println(sparkTimer); + Stopwatch zipperTimer = Stopwatch.newAndStart("Zipper"); + Main.run(prePTA, PCMs); + zipperTimer.stop(); + System.out.println(zipperTimer); + extraStats(); + } + + protected void extraStats() { + int[] RM = new int[1], PCN = new int[1], NPCN = new int[1]; + int[] totalN = new int[1]; + for (ContextMethod momc : prePTA.getReachableMethods()) { + SootMethod method = momc.method(); + Set nodes = new HashSet<>(); + + if (!PTAUtils.hasBody(method)) { + return; + } + MethodPAG srcmpag = pag.getMethodPAG(method); + QueueReader reader = srcmpag.getInternalReader().clone(); + while (reader.hasNext()) { + Node from = reader.next(), to = reader.next(); + if (from instanceof LocalVarNode) { + nodes.add(((VarNode) from).getVariable()); + } else if (from instanceof AllocNode) { + nodes.add(((AllocNode) from).getNewExpr()); + } else if (from instanceof FieldRefNode) { + FieldRefNode fr = (FieldRefNode) from; + VarNode base = fr.getBase(); + if (base instanceof LocalVarNode) { + nodes.add(base.getVariable()); + } + } + + if (to instanceof LocalVarNode) { + nodes.add(((VarNode) to).getVariable()); + } else if (to instanceof FieldRefNode) { + FieldRefNode fr = (FieldRefNode) to; + VarNode base = fr.getBase(); + if (base instanceof LocalVarNode) { + nodes.add(base.getVariable()); + } + } + } + for (final Stmt s : srcmpag.getInvokeStmts()) { + AbstractInvokeExpr ie = s.getInvokeExpr(); + int numArgs = ie.getArgCount(); + for (int i = 0; i < numArgs; i++) { + Value arg = ie.getArg(i); + if (!(arg.getType() instanceof ReferenceType) || arg instanceof NullConstant) { + continue; + } + nodes.add(arg); + } + + if (s instanceof JAssignStmt) { + Value dest = ((JAssignStmt) s).getLeftOp(); + if (dest.getType() instanceof ReferenceType) { + nodes.add(dest); + } + } + if (ie instanceof AbstractInstanceInvokeExpr) { + AbstractInstanceInvokeExpr iie = (AbstractInstanceInvokeExpr) ie; + Local base = iie.getBase(); + nodes.add(base); + } + } + + if (PCMs.contains(method)) { + PCN[0] += nodes.size(); + } else { + NPCN[0] += nodes.size(); + } + RM[0]++; + + totalN[0] += nodes.size(); + } + RM[0]--; // For the FakeMain method. + System.out.println("#ReachableMethod:" + RM[0]); + System.out.println("#FCSM:" + PCMs.size()); + System.out.println("#PCSM:0"); + System.out.println("#CIM:" + (RM[0] - PCMs.size())); + System.out.println("#CIN: " + NPCN[0]); + System.out.println("#CSN: " + PCN[0]); + System.out.println("totalN: " + totalN[0]); + } +} diff --git a/sootup.qilin/src/main/java/qilin/stat/AbstractStat.java b/sootup.qilin/src/main/java/qilin/stat/AbstractStat.java new file mode 100644 index 00000000000..351c21f345b --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/stat/AbstractStat.java @@ -0,0 +1,23 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.stat; + +public interface AbstractStat { + void export(Exporter exporter); +} diff --git a/sootup.qilin/src/main/java/qilin/stat/AliasStat.java b/sootup.qilin/src/main/java/qilin/stat/AliasStat.java new file mode 100644 index 00000000000..149e7729781 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/stat/AliasStat.java @@ -0,0 +1,190 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.stat; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import qilin.core.PTA; +import qilin.core.pag.FieldRefNode; +import qilin.core.pag.LocalVarNode; +import qilin.core.pag.MethodPAG; +import qilin.core.pag.Node; +import qilin.core.pag.SparkField; +import qilin.core.pag.VarNode; +import qilin.core.sets.PointsToSet; +import qilin.util.Pair; +import qilin.util.Util; +import qilin.util.queue.QueueReader; +import sootup.core.jimple.basic.Local; +import sootup.core.model.SootMethod; + +public class AliasStat implements AbstractStat { + private final PTA pta; + Map> assignMap = new HashMap<>(); + Map>> globalMap = new HashMap<>(); + private int intraAlias = 0, intraAlias_incstst = 0, globalAlias = 0, globalAlias_incstst = 0; + private int intraAlias_app = 0, + intraAlias_incstst_app = 0, + globalAlias_app = 0, + globalAlias_incstst_app = 0; + + public AliasStat(PTA pta) { + this.pta = pta; + } + + private Pair recordAndComputeIntraAliases(Set reachableMethods) { + int intraAlias = 0; + int intraAlias_incstst = 0; + for (SootMethod m : reachableMethods) { + Map>> localMap = new HashMap<>(); + MethodPAG srcmpag = pta.getPag().getMethodPAG(m); + QueueReader reader = srcmpag.getInternalReader().clone(); + while (reader.hasNext()) { + Node from = reader.next(), to = reader.next(); + if (from instanceof LocalVarNode) { + if (to instanceof LocalVarNode) { + if (!(((VarNode) from).getVariable() instanceof Local)) continue; + if (!(((VarNode) to).getVariable() instanceof Local)) continue; + Util.addToMap(assignMap, (LocalVarNode) from, (LocalVarNode) to); + Util.addToMap(assignMap, (LocalVarNode) to, (LocalVarNode) from); + } else if (to instanceof FieldRefNode) { + FieldRefNode fr = (FieldRefNode) to; + LocalVarNode base = (LocalVarNode) fr.getBase(); + if (!(base.getVariable() instanceof Local)) continue; + addToMap(globalMap, fr.getField(), true, base); + addToMap(localMap, fr.getField(), true, base); + } // else//local-global + } else if (from instanceof FieldRefNode) { + FieldRefNode fr = (FieldRefNode) from; + LocalVarNode base = (LocalVarNode) fr.getBase(); + if (!(base.getVariable() instanceof Local)) continue; + addToMap(globalMap, fr.getField(), false, base); + addToMap(localMap, fr.getField(), false, base); + } // else//global-local or new + } + + int methodAlias = 0, methodAlias_incstst = 0; + for (Map> subMap : localMap.values()) { + Set storeSet = subMap.getOrDefault(true, Collections.emptySet()); + Set loadSet = subMap.getOrDefault(false, Collections.emptySet()); + int stld = + checkAlias(storeSet, loadSet, assignMap) + checkAlias(loadSet, storeSet, assignMap); + int stst = checkAlias(storeSet, storeSet, assignMap); + methodAlias += stld; + methodAlias_incstst += stld + stst; + } + + intraAlias += methodAlias; + intraAlias_incstst += methodAlias_incstst; + } + return new Pair<>(intraAlias, intraAlias_incstst); + } + + private Pair computeInterAliases() { + int globalAlias = 0; + int globalAlias_incstst = 0; + for (Map> subMap : globalMap.values()) { + Set storeSet = subMap.getOrDefault(true, Collections.emptySet()); + Set loadSet = subMap.getOrDefault(false, Collections.emptySet()); + int stld = + checkAlias(storeSet, loadSet, assignMap) + checkAlias(loadSet, storeSet, assignMap); + int stst = checkAlias(storeSet, storeSet, assignMap); + globalAlias += stld; + globalAlias_incstst += stld + stst; + } + return new Pair<>(globalAlias, globalAlias_incstst); + } + + private int checkAlias( + Set set1, + Set set2, + Map> exclMap) { + int num = 0; + for (LocalVarNode l1 : set1) { + Set exclSet = exclMap.getOrDefault(l1, Collections.emptySet()); + int l1Hashcode = l1.hashCode(); + for (LocalVarNode l2 : set2) { + int l2Hashcode = l2.hashCode(); + if (l2Hashcode <= l1Hashcode || exclSet.contains(l2)) { + continue; + } + if (checkAlias(l1, l2)) { + num++; + } + } + } + return num; + } + + private boolean checkAlias(LocalVarNode l1, LocalVarNode l2) { + PointsToSet pts1 = pta.reachingObjects(l1.getMethod(), (Local) l1.getVariable()); + PointsToSet pts2 = pta.reachingObjects(l2.getMethod(), (Local) l2.getVariable()); + return pts1.hasNonEmptyIntersection(pts2); + } + + public static boolean addToMap(Map>> m, K key1, T key2, V value) { + Map> subMap = m.computeIfAbsent(key1, k -> new HashMap<>()); + return Util.addToMap(subMap, key2, value); + } + + public void aliasesProcessing() { + Collection reachableMethods = pta.getNakedReachableMethods(); + Pair r1 = + recordAndComputeIntraAliases( + reachableMethods.stream() + .filter(pta.getScene()::isApplicationMethod) + .collect(Collectors.toSet())); + this.intraAlias_app = r1.getFirst(); + this.intraAlias_incstst_app = r1.getSecond(); + Pair r2 = computeInterAliases(); + this.globalAlias_app = r2.getFirst(); + this.globalAlias_incstst_app = r2.getSecond(); + Pair r3 = + recordAndComputeIntraAliases( + reachableMethods.stream() + .filter(m -> !pta.getScene().isApplicationMethod(m)) + .collect(Collectors.toSet())); + this.intraAlias = this.intraAlias_app + r3.getFirst(); + this.intraAlias_incstst = this.intraAlias_incstst_app + r3.getSecond(); + Pair r4 = computeInterAliases(); + this.globalAlias = r4.getFirst(); + this.globalAlias_incstst = r4.getSecond(); + } + + public int getGlobalAliasesIncludingStSt() { + return globalAlias_incstst; + } + + @Override + public void export(Exporter exporter) { + aliasesProcessing(); + exporter.collectMetric("#intraAlias(App):", String.valueOf(intraAlias_app)); + exporter.collectMetric("#intraAlias_incstst(App):", String.valueOf(intraAlias_incstst_app)); + exporter.collectMetric("#globalAlias(App):", String.valueOf(globalAlias_app)); + exporter.collectMetric("#globalAlias_incstst(App):", String.valueOf(globalAlias_incstst_app)); + exporter.collectMetric("#intraAlias:", String.valueOf(intraAlias)); + exporter.collectMetric("#intraAlias_incstst:", String.valueOf(intraAlias_incstst)); + exporter.collectMetric("#globalAlias:", String.valueOf(globalAlias)); + exporter.collectMetric("#globalAlias_incstst:", String.valueOf(globalAlias_incstst)); + } +} diff --git a/sootup.qilin/src/main/java/qilin/stat/BenchmarkStat.java b/sootup.qilin/src/main/java/qilin/stat/BenchmarkStat.java new file mode 100644 index 00000000000..133a7a2336b --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/stat/BenchmarkStat.java @@ -0,0 +1,83 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.stat; + +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import qilin.CoreConfig; +import qilin.core.PTA; +import qilin.core.PTAScene; +import sootup.core.model.SootClass; +import sootup.core.types.ClassType; +import sootup.core.views.View; + +public class BenchmarkStat implements AbstractStat { + private final PTA pta; + + private int classes = 0; + private int appClasses = 0; + private int phantomClasses = 0; + private int libClasses = 0; + private Set reachableClasses; + private Set reachableAppClasses; + int libReachableClasses = 0; + + public BenchmarkStat(PTA pta) { + this.pta = pta; + init(); + } + + private void init() { + View view = pta.getView(); + reachableClasses = + pta.getNakedReachableMethods().stream() + .map( + m -> { + ClassType classType = m.getDeclaringClassType(); + return view.getClass(classType); + }) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toSet()); + reachableAppClasses = + reachableClasses.stream().filter(SootClass::isApplicationClass).collect(Collectors.toSet()); + PTAScene scene = pta.getScene(); + classes = scene.getClasses().size(); + appClasses = scene.getApplicationClasses().size(); + phantomClasses = scene.getPhantomClasses().size(); + libClasses = classes - appClasses - phantomClasses; + libReachableClasses = (reachableClasses.size() - reachableAppClasses.size() - 1); // -FakeMain + } + + @Override + public void export(Exporter exporter) { + exporter.collectMetric("#Class:", String.valueOf(classes)); + exporter.collectMetric("#Appclass:", String.valueOf(appClasses)); + exporter.collectMetric("#Libclass:", String.valueOf(libClasses)); + exporter.collectMetric("#Phantomclass:", String.valueOf(phantomClasses)); + exporter.collectMetric("#Class(reachable):", String.valueOf(reachableClasses.size())); + exporter.collectMetric("#Appclass(reachable):", String.valueOf(reachableAppClasses.size())); + exporter.collectMetric("#Libclass(reachable):", String.valueOf(libReachableClasses)); + + if (CoreConfig.v().getOutConfig().dumpStats) { + exporter.dumpClassTypes(pta.getScene().getClasses()); + } + } +} diff --git a/sootup.qilin/src/main/java/qilin/stat/CallGraphStat.java b/sootup.qilin/src/main/java/qilin/stat/CallGraphStat.java new file mode 100644 index 00000000000..eeaffa6692e --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/stat/CallGraphStat.java @@ -0,0 +1,228 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.stat; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import qilin.CoreConfig; +import qilin.core.PTA; +import qilin.core.PTAScene; +import qilin.core.builder.FakeMainFactory; +import qilin.core.builder.callgraph.Edge; +import qilin.core.builder.callgraph.OnFlyCallGraph; +import qilin.core.pag.ContextMethod; +import qilin.core.pag.ContextVarNode; +import qilin.core.pag.LocalVarNode; +import sootup.core.model.SootClass; +import sootup.core.model.SootMethod; + +public class CallGraphStat implements AbstractStat { + private final PTA pta; + + private final Set reachableMethods = new HashSet<>(); + private int reachableStatic = 0; + private final Set reachableParameterizedMethods = new HashSet<>(); + private final Set reachableAppParameterizedMethods = new HashSet<>(); + private final Set reachableAppMethods = new HashSet<>(); + private int reachableAppStatic = 0; + private int CSCallEdges = 0; + private int CSStaticToStatic = 0; + private int CSStaticToInstance = 0; + private int CSInstanceToStatic = 0; + private int CSInstancetoInstance = 0; + private int CSApp2lib = 0; + private int CSApp2app = 0; + private int CSLib2lib = 0; + private int CSLib2app = 0; + private int CICallEdges = 0; + private int CIApp2lib = 0; + private int CIApp2app = 0; + private int CILib2lib = 0; + private int CILib2app = 0; + private int CIStaticToStatic = 0; + private int CIStaticToInstance = 0; + private int CIInstanceToStatic = 0; + private int CIInstancetoInstance = 0; + private int allMethods = 0; + + public CallGraphStat(PTA pta) { + this.pta = pta; + init(); + } + + private void init() { + // stat method numbers + Collection clazzs = pta.getView().getClasses(); + for (SootClass clazz : clazzs) { + allMethods += clazz.getMethods().size(); + } + // + OnFlyCallGraph csCallGraph = pta.getCgb().getCallGraph(); + CSCallEdges = csCallGraph.size(); + PTAScene scene = pta.getScene(); + for (final ContextMethod momc : pta.getCgb().getReachableMethods()) { + final SootMethod m = momc.method(); + boolean toApp = scene.isApplicationMethod(m); + reachableParameterizedMethods.add(momc); + reachableMethods.add(m); + // if (m.toString().equals("")) { + // System.out.println(PTAUtils.getMethodBody(m)); + // } + + if (toApp) { + reachableAppParameterizedMethods.add(momc); + reachableAppMethods.add(momc.method()); + } + + for (Iterator iterator = csCallGraph.edgesInto(momc); iterator.hasNext(); ) { + Edge e = iterator.next(); + final SootMethod srcm = e.getSrc().method(); + boolean fromApp = scene.isApplicationMethod(srcm); + if (fromApp && toApp) { + CSApp2app++; + } else if (fromApp) { + CSApp2lib++; + } else if (!toApp) { + CSLib2lib++; + } else { + CSLib2app++; + } + if (e.src().isStatic()) { + if (e.isStatic()) { + CSStaticToStatic++; + } else { + CSStaticToInstance++; + } + } else if (e.isStatic()) { + CSInstanceToStatic++; + } else { + CSInstancetoInstance++; + } + } + } + OnFlyCallGraph ciCallGraph = pta.getCallGraph(); + CICallEdges = ciCallGraph.size(); + for (SootMethod sm : reachableMethods) { + boolean toApp = scene.isApplicationMethod(sm); + if (sm.isStatic()) { + reachableStatic++; + if (toApp) { + reachableAppStatic++; + } + } + for (Iterator iterator = + ciCallGraph.edgesInto(new ContextMethod(sm, pta.emptyContext())); + iterator.hasNext(); ) { + Edge e = iterator.next(); + final SootMethod srcm = e.getSrc().method(); + boolean fromApp = scene.isApplicationMethod(srcm); + if (fromApp && toApp) { + CIApp2app++; + } else if (fromApp) { + CIApp2lib++; + } else if (!toApp) { + CILib2lib++; + } else { + CILib2app++; + } + + if (e.src().isStatic()) { + if (e.isStatic()) { + CIStaticToStatic++; + } else { + CIStaticToInstance++; + } + } else if (e.isStatic()) { + CIInstanceToStatic++; + } else { + CIInstancetoInstance++; + } + } + } + } + + private long thisReceiverCount() { + return pta.getCgb().getReceiverToSitesMap().entrySet().stream() + .filter(e -> e.getKey() instanceof ContextVarNode) + .filter(e -> e.getKey().base() instanceof LocalVarNode) + .filter(e -> ((LocalVarNode) e.getKey().base()).isThis()) + .count(); + } + + @Override + public void export(Exporter exporter) { + exporter.collectMetric("#Method (Static):", String.valueOf(allMethods - 1)); // -fakeMain + exporter.collectMetric( + "#Reachable Method (CI):", String.valueOf(reachableMethods.size() - 1)); // -fakeMain + exporter.collectMetric( + "\t#Reachable-Static Method (CI):", String.valueOf(reachableStatic - 1)); // -fakeMain + exporter.collectMetric( + "#Reachable Method (CS):", + String.valueOf(reachableParameterizedMethods.size() - 1)); // -fakeMain + exporter.collectMetric( + "#Reachable App Method (CI):", String.valueOf(reachableAppMethods.size())); + exporter.collectMetric( + "\t#Reachable-App-Static Method (CI):", String.valueOf(reachableAppStatic)); + exporter.collectMetric( + "#Reachable App Method (CS):", String.valueOf(reachableAppParameterizedMethods.size())); + exporter.collectMetric( + "#Call Edge(CI):", String.valueOf(CICallEdges - FakeMainFactory.implicitCallEdges)); + exporter.collectMetric( + "\t#Static-Static Call Edge(CI):", + String.valueOf(CIStaticToStatic - FakeMainFactory.implicitCallEdges)); + exporter.collectMetric("\t#Static-Instance Call Edge(CI):", String.valueOf(CIStaticToInstance)); + exporter.collectMetric("\t#Instance-Static Call Edge(CI):", String.valueOf(CIInstanceToStatic)); + exporter.collectMetric( + "\t#Instance-Instance Call Edge(CI):", String.valueOf(CIInstancetoInstance)); + exporter.collectMetric("\t#Application-Application Call Edge(CI):", String.valueOf(CIApp2app)); + exporter.collectMetric("\t#Application-Library Call Edge(CI):", String.valueOf(CIApp2lib)); + exporter.collectMetric("\t#Library-Application Call Edge(CI):", String.valueOf(CILib2app)); + exporter.collectMetric("\t#Library-Library Call Edge(CI):", String.valueOf(CILib2lib)); + exporter.collectMetric( + "#Call Edge(CS):", String.valueOf(CSCallEdges - FakeMainFactory.implicitCallEdges)); + exporter.collectMetric( + "\t#Static-Static Call Edge(CS):", + String.valueOf(CSStaticToStatic - FakeMainFactory.implicitCallEdges)); + exporter.collectMetric("\t#Static-Instance Call Edge(CS):", String.valueOf(CSStaticToInstance)); + exporter.collectMetric("\t#Instance-Static Call Edge(CS):", String.valueOf(CSInstanceToStatic)); + exporter.collectMetric( + "\t#Instance-Instance Call Edge(CS):", String.valueOf(CSInstancetoInstance)); + exporter.collectMetric("\t#Application-Application Call Edge(CS):", String.valueOf(CSApp2app)); + exporter.collectMetric("\t#Application-Library Call Edge(CS):", String.valueOf(CSApp2lib)); + exporter.collectMetric("\t#Library-Application Call Edge(CS):", String.valueOf(CSLib2app)); + exporter.collectMetric("\t#Library-Library Call Edge(CS):", String.valueOf(CSLib2lib)); + exporter.collectMetric( + "#receivers:", String.valueOf(pta.getCgb().getReceiverToSitesMap().size())); + exporter.collectMetric("\t#thisreceivers:", String.valueOf(thisReceiverCount())); + exporter.collectMetric( + "#avg p2s size for virtualcalls:", + String.valueOf( + (CSCallEdges - CSStaticToStatic - CSInstanceToStatic) + * 1.0 + / pta.getCgb().getReceiverToSitesMap().size())); + if (CoreConfig.v().getOutConfig().dumpStats) { + exporter.dumpReachableMethods(reachableMethods); + exporter.dumpAppReachableMethods(reachableAppMethods); + exporter.dumpInsensCallGraph(pta.getCallGraph()); + } + } +} diff --git a/sootup.qilin/src/main/java/qilin/stat/Exporter.java b/sootup.qilin/src/main/java/qilin/stat/Exporter.java new file mode 100644 index 00000000000..e1b92fcadbd --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/stat/Exporter.java @@ -0,0 +1,314 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.stat; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.*; +import qilin.CoreConfig; +import qilin.core.PTA; +import qilin.core.builder.callgraph.Edge; +import qilin.core.builder.callgraph.OnFlyCallGraph; +import qilin.core.pag.AllocNode; +import qilin.core.pag.LocalVarNode; +import qilin.core.pag.Parm; +import qilin.core.sets.PointsToSet; +import qilin.util.Util; +import sootup.core.jimple.common.expr.AbstractInvokeExpr; +import sootup.core.jimple.common.expr.JCastExpr; +import sootup.core.jimple.common.stmt.JAssignStmt; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.SootClass; +import sootup.core.model.SootMethod; +import sootup.core.types.Type; + +public class Exporter { + private final PTA pta; + private final String metrics = "Metrics.csv"; + private final String staticFPTs = "StaticFieldPointsTo.csv"; + + private static final int STATLENGTH = 50; + private final StringBuffer report = new StringBuffer(); + + public Exporter(PTA pta) { + this.pta = pta; + } + + public void addLine(String str) { + report.append(str).append('\n'); + } + + private String makeUp(String string) { + final String makeUpString = " "; + String tmp = ""; + for (int i = 0; i < Math.max(0, STATLENGTH - string.length()); ++i) { + tmp = tmp.concat(makeUpString); + } + // StringBuilder ret = new StringBuilder(); + // ret.append(makeUpString.repeat(Math.max(0, STATLENGTH - string.length()))); + return string + tmp; + } + + public void collectMetric(String desc, String value) { + addLine(makeUp(desc) + value); + } + + private String getFilePath(String fileName) { + String finalPath = CoreConfig.v().getOutConfig().outDir; + finalPath = + finalPath + + File.separator + + CoreConfig.v().getAppConfig().MAIN_CLASS + + File.separator + + CoreConfig.v().getPtaConfig().ptaName + + File.separator; + File file = new File(finalPath); + if (!file.exists()) { + file.mkdirs(); + } + finalPath = finalPath + fileName; + return finalPath; + } + + private void dumpMethods(Collection methods, String fileName) { + StringBuilder builder = new StringBuilder(); + for (SootMethod sm : methods) { + String sig = sm.getSignature().toString(); + sig = Util.stripQuotes(sig); + builder.append(sig); + builder.append("\n"); + } + String finalPath = getFilePath(fileName); + Util.writeToFile(finalPath, builder.toString()); + } + + public void dumpReachableMethods(Collection reachables) { + String reachMethods = "Reachable.csv"; + dumpMethods(reachables, reachMethods); + } + + public void dumpAppReachableMethods(Collection appReachables) { + String appReachMethods = "AppReachable.csv"; + dumpMethods(appReachables, appReachMethods); + } + + public void dumpSingleCallMethods(Collection singleCallMs) { + String singleCalls = "SingleCallMethods.csv"; + dumpMethods(singleCallMs, singleCalls); + } + + public void dumpSingleReceiverMethods(Collection singleReceiverMs) { + String singleReceivers = "SingleReceiverMethods.csv"; + dumpMethods(singleReceiverMs, singleReceivers); + } + + public void dumpSingleCallSingleReceiverMethods( + Collection singleCallSingleReceiverMs) { + String singleCallSingleReceivers = "SingleCallSingleReceiverMethods.csv"; + dumpMethods(singleCallSingleReceiverMs, singleCallSingleReceivers); + } + + public void dumpClassTypes(Collection classes) { + StringBuilder builder = new StringBuilder(); + for (SootClass sc : classes) { + builder.append(sc.getName()); + builder.append("\n"); + } + String classTypes = "ClassType.csv"; + String finalPath = getFilePath(classTypes); + Util.writeToFile(finalPath, builder.toString()); + } + + public void dumpPolyCalls(Map polys) { + StringBuilder builder = new StringBuilder(); + for (AbstractInvokeExpr ie : polys.keySet()) { + SootMethod tgt = pta.getView().getMethod(ie.getMethodSignature()).get(); + String polySig = + polys.get(ie).getSignature() + + "/" + + tgt.getDeclaringClassType() + + "." + + tgt.getName() + + "\n"; + builder.append(polySig); + } + String polyCalls = "PolyCalls.csv"; + String finalPath = getFilePath(polyCalls); + Util.writeToFile(finalPath, builder.toString()); + } + + public void dumpMayFailCasts(Map> casts) { + StringBuilder builder = new StringBuilder(); + for (SootMethod sm : casts.keySet()) { + for (Stmt stmt : casts.get(sm)) { + JAssignStmt as = (JAssignStmt) stmt; + JCastExpr ce = (JCastExpr) as.getRightOp(); + final Type targetType = ce.getType(); + builder.append(sm.toString()); + builder.append("\t"); + builder.append(targetType.toString()); + builder.append("\t"); + builder.append(sm).append("/").append(ce.getOp().toString()); + builder.append("\t"); + builder.append(sm).append("/").append(as.getLeftOp().toString()); + builder.append("\n"); + } + } + String mayFailCasts = "MayFailCasts.csv"; + String finalPath = getFilePath(mayFailCasts); + Util.writeToFile(finalPath, builder.toString()); + } + + public void dumpMethodThrowPointsto(Map m2pts) { + String methodThrowPts = "MethodThrowPointsTo.csv"; + String finalPath = getFilePath(methodThrowPts); + try { + File mfile = new File(finalPath); + mfile.delete(); + mfile.createNewFile(); + BufferedWriter writer = new BufferedWriter(new FileWriter(mfile, true)); + for (SootMethod sm : m2pts.keySet()) { + PointsToSet pts = m2pts.get(sm).toCIPointsToSet(); + for (Iterator it = pts.iterator(); it.hasNext(); ) { + AllocNode n = it.next(); + StringBuilder builder = new StringBuilder(); + builder.append(n.toString()); + builder.append("\t"); + String sig = Util.stripQuotes(sm.getSignature().toString()); + builder.append(sig); + builder.append("\n"); + try { + writer.write(builder.toString()); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + writer.flush(); + writer.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void dumpInsensCallGraph(OnFlyCallGraph ciCallGraph) { + String insensCallGraphEdges = "InsensCallGraphEdges.csv"; + String finalPath = getFilePath(insensCallGraphEdges); + try { + File mfile = new File(finalPath); + mfile.delete(); + mfile.createNewFile(); + BufferedWriter writer = new BufferedWriter(new FileWriter(mfile, true)); + for (Edge edge : ciCallGraph) { + String srcSig = Util.stripQuotes(edge.src().getSignature().toString()); + String dstSig = Util.stripQuotes(edge.tgt().getSignature().toString()); + String str = edge.srcStmt() + " in method " + srcSig + "\t" + dstSig + "\n"; + writer.write(str); + } + writer.flush(); + writer.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void dumpReachableLocalVars(Collection lvns) { + StringBuilder builder = new StringBuilder(); + for (LocalVarNode lvn : lvns) { + String varName = getDoopVarName(lvn); + builder.append(varName).append("\n"); + } + String insensReachVars = "InsensReachVars.csv"; + String finalPath = getFilePath(insensReachVars); + Util.writeToFile(finalPath, builder.toString()); + } + + public void dumpReachableLocalVarsNoNative(Collection lvns) { + StringBuilder builder = new StringBuilder(); + for (LocalVarNode lvn : lvns) { + String varName = getDoopVarName(lvn); + builder.append(varName).append("\n"); + } + String insensReachVars = "InsensReachVarsNoNatives.csv"; + String finalPath = getFilePath(insensReachVars); + Util.writeToFile(finalPath, builder.toString()); + } + + private String getDoopVarName(LocalVarNode lvn) { + SootMethod m = lvn.getMethod(); + Object v = lvn.getVariable(); + String varName = v.toString(); + if (v instanceof Parm) { + Parm parm = (Parm) v; + if (parm.isThis()) { + varName = "@this"; + } else if (parm.isReturn()) { + + } else if (parm.isThrowRet()) { + + } else { + varName = "@parameter" + parm.getIndex(); + } + } + return m.getSignature() + "/" + varName; + } + + public void dumpInsensPointsTo(Collection lvns, PTA pta) { + String insensVarPTs = "InsensVarPointsTo.csv"; + String finalPath = getFilePath(insensVarPTs); + try { + File mfile = new File(finalPath); + mfile.delete(); + mfile.createNewFile(); + BufferedWriter writer = new BufferedWriter(new FileWriter(mfile, true)); + for (LocalVarNode lvn : lvns) { + String varName = getDoopVarName(lvn); + final Set callocSites = new HashSet<>(); + PointsToSet cpts = pta.reachingObjects(lvn).toCIPointsToSet(); + for (Iterator it = cpts.iterator(); it.hasNext(); ) { + AllocNode heap = it.next(); + callocSites.add(heap); + } + for (AllocNode heap : callocSites) { + String str = heap.getNewExpr() + "\t" + varName + "\n"; + if (heap.getMethod() != null) { + str = heap.getMethod() + "/" + heap.getNewExpr() + "\t" + varName + "\n"; + } + writer.write(str); + } + } + writer.flush(); + writer.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public String report() { + String tmp = report.toString(); + if (CoreConfig.v().getOutConfig().dumpStats) { + String statistics = "Statistics.txt"; + String finalPath = getFilePath(statistics); + Util.writeToFile(finalPath, tmp); + } + return tmp; + } +} diff --git a/sootup.qilin/src/main/java/qilin/stat/IEvaluator.java b/sootup.qilin/src/main/java/qilin/stat/IEvaluator.java new file mode 100644 index 00000000000..13c46aecc21 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/stat/IEvaluator.java @@ -0,0 +1,27 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.stat; + +public interface IEvaluator { + int GB = 1024 * 1024 * 1024; + + void begin(); + + void end(); +} diff --git a/sootup.qilin/src/main/java/qilin/stat/PAGStat.java b/sootup.qilin/src/main/java/qilin/stat/PAGStat.java new file mode 100644 index 00000000000..bf348472651 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/stat/PAGStat.java @@ -0,0 +1,80 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.stat; + +import java.util.Map; +import java.util.Set; +import qilin.core.PTA; +import qilin.core.pag.*; + +public class PAGStat implements AbstractStat { + private final PTA pta; + private final PAG pag; + + private int newEdges = 0; // v = new O; + private int simpleEdges = 0; // include assign, global_assign, and o.f assign. + private int hloadEdges = 0; // v = o.f; + private int hstoreEdges = 0; // o.f = v; + private int storeEdges = 0; // v.f = v; + private int loadEdges = 0; // v = v.f; + + public PAGStat(PTA pta) { + this.pta = pta; + this.pag = pta.getPag(); + init(); + } + + private void init() { + for (Set s : pag.getAlloc().values()) { + newEdges += s.size(); + } + for (Map.Entry> e : pag.getSimple().entrySet()) { + Set tagets = e.getValue(); + int nt = tagets.size(); + simpleEdges += nt; + if (e.getKey() instanceof ContextField) { + hloadEdges += nt; + } else { + for (ValNode v : tagets) { + if (v instanceof ContextField) { + hstoreEdges++; + } + } + } + } + for (Map.Entry> s : pag.getStoreInv().entrySet()) { + storeEdges += s.getValue().size(); + } + for (Map.Entry> s : pag.getLoad().entrySet()) { + loadEdges += s.getValue().size(); + } + } + + @Override + public void export(Exporter exporter) { + exporter.collectMetric("#Alloc-pag-edge:", String.valueOf(newEdges)); + exporter.collectMetric("#Simple-pag-edge:", String.valueOf(simpleEdges)); + exporter.collectMetric( + "\t#Local-to-Local:", String.valueOf((simpleEdges - hloadEdges - hstoreEdges))); + exporter.collectMetric("\t#Field-to-Local:", String.valueOf(hloadEdges)); + exporter.collectMetric("\t#Local-to-Field:", String.valueOf(hstoreEdges)); + exporter.collectMetric("#Store-pag-edge:", String.valueOf(storeEdges)); + exporter.collectMetric("#Load-pag-edge:", String.valueOf(loadEdges)); + } +} diff --git a/sootup.qilin/src/main/java/qilin/stat/PTAEvaluator.java b/sootup.qilin/src/main/java/qilin/stat/PTAEvaluator.java new file mode 100644 index 00000000000..62cdfd63f3f --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/stat/PTAEvaluator.java @@ -0,0 +1,112 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.stat; + +import qilin.CoreConfig; +import qilin.core.PTA; + +/** + * Gather stats on the performance and precision of a PTA run. + * + *

Each new run of the PTA will over-write stats + * + *

- Wall time (sec) - Memory (max, current) before and after - Reachable methods (context and + * no-context) - Total reachable casts - Reachable casts that may fail - Call graph edges - Context + * sensitive call graph edges - Total reachable virtual call sites - Polymorphic virtual call sites + * (sites with >1 target methods) - Number of pointers (local and global) - Total points to sets + * size (local and global) context insensitive (convert to alloc site) + */ +public class PTAEvaluator implements IEvaluator { + protected final RuntimeStat runtimeStat; + protected final Exporter exporter; + protected final PTA pta; + + public PTAEvaluator(PTA pta) { + this.pta = pta; + runtimeStat = new RuntimeStat(); + exporter = new Exporter(pta); + } + + /** Note the start of a qilin.pta run. */ + @Override + public void begin() { + Runtime runtime = Runtime.getRuntime(); // Getting the runtime reference + // from system + exporter.addLine(" ====== Memory Usage ======"); + exporter.collectMetric( + "Used Memory Before:", + (runtime.totalMemory() - runtime.freeMemory()) / GB + " GB"); // Print + // used memory + exporter.collectMetric( + "Free Memory Before:", runtime.freeMemory() / GB + " GB"); // Print free memory + exporter.collectMetric( + "Total Memory Before:", runtime.totalMemory() / GB + " GB"); // Print total available memory + exporter.collectMetric( + "Max Memory Before:", runtime.maxMemory() / GB + " GB"); // Print Maximum available memory + exporter.collectMetric("Analysis: ", CoreConfig.v().getPtaConfig().ptaName); + runtimeStat.begin(); + } + + /** Note the end of a qilin.pta run. */ + @Override + public void end() { + // done with processing + runtimeStat.end(); + /** all method reachable from the harness main */ + PAGStat pagStat = new PAGStat(pta); + BenchmarkStat benchmarkStat = new BenchmarkStat(pta); + CallGraphStat callGraphStat = new CallGraphStat(pta); + TypeClientStat typeClientStat = new TypeClientStat(pta); + PointsToStat ptsStat = new PointsToStat(pta); + AliasStat aliasStat = new AliasStat(pta); + YummyStat yummyStat = new YummyStat(pta); + runtimeStat.export(exporter); + // memory stats + Runtime runtime = Runtime.getRuntime(); // Getting the runtime reference + // from system + exporter.collectMetric( + "Used Memory After:", (runtime.totalMemory() - runtime.freeMemory()) / GB + " GB"); // Print + // used memory + exporter.collectMetric( + "Free Memory After:", runtime.freeMemory() / GB + " GB"); // Print free memory + exporter.collectMetric( + "Total Memory After:", runtime.totalMemory() / GB + " GB"); // Print total available memory + exporter.collectMetric( + "Max Memory After:", runtime.maxMemory() / GB + " GB"); // Print Maximum available memory + exporter.addLine(" ====== Yummy ======"); + yummyStat.export(exporter); + exporter.addLine(" ====== Call Graph ======"); + callGraphStat.export(exporter); + exporter.addLine(" ====== Statements ======"); + typeClientStat.export(exporter); + exporter.addLine(" ====== Nodes ======"); + ptsStat.export(exporter); + exporter.addLine(" ====== Assignments ======"); + pagStat.export(exporter); + exporter.addLine(" ====== Aliases ======"); + aliasStat.export(exporter); + exporter.addLine(" ====== Classes ======"); + benchmarkStat.export(exporter); + } + + @Override + public String toString() { + return exporter.report(); + } +} diff --git a/sootup.qilin/src/main/java/qilin/stat/PointsToStat.java b/sootup.qilin/src/main/java/qilin/stat/PointsToStat.java new file mode 100644 index 00000000000..f59d4c3e155 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/stat/PointsToStat.java @@ -0,0 +1,335 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.stat; + +import com.google.common.collect.Sets; +import java.util.*; +import qilin.CoreConfig; +import qilin.core.PTA; +import qilin.core.builder.MethodNodeFactory; +import qilin.core.context.Context; +import qilin.core.pag.*; +import qilin.core.sets.PointsToSet; +import qilin.util.PTAUtils; +import qilin.util.Triple; +import sootup.core.jimple.basic.Local; +import sootup.core.model.SootClass; +import sootup.core.model.SootMethod; +import sootup.core.signatures.FieldSignature; +import sootup.core.types.ClassType; +import sootup.core.types.ReferenceType; +import sootup.core.types.Type; + +public class PointsToStat implements AbstractStat { + private final PTA pta; + private final PAG pag; + private int contextCnt = 0; + private double avgCtxPerMthd = 0.0; + + private int ciAllocs = 0; + private int csAllocs = 0; + private int totalGlobalPointers = 0; + private int totalGlobalPointsToCi = 0; + private int totalGlobalPointsToCs = 0; + private int appGlobalPointers = 0; + private int appGlobalPointsToCi = 0; + private int appGlobalPointsToCs = 0; + private int totalLocalPointersCi = 0; + private int totalLocalPointersCs = 0; + private int totalLocalCiToCi = 0; + private int totalLocalCiToCs = 0; + private int totalLocalCsToCi = 0; + private int totalLocalCsToCs = 0; + private int appLocalPointersCi = 0; + private int appLocalPointersCs = 0; + private int appLocalCiToCi = 0; + private int appLocalCiToCs = 0; + private int appLocalCsToCi = 0; + private int appLocalCsToCs = 0; + private int totalFieldPointsToCs = 0; + private int methodThrowCnt = 0; + + private final Map methodThrowPts; + private final Set mLocalVarNodes = new HashSet<>(); + private int ptsCnt = 0; + private int varCnt = 0; + private final Set mLocalVarNodesNoNative = new HashSet<>(); + private int ptsCntNoNative = 0; + private int varCntNoNative = 0; + + public PointsToStat(PTA pta) { + this.pta = pta; + this.pag = pta.getPag(); + methodThrowPts = new HashMap<>(); + init(); + } + + private final Set handledNatives = + Sets.newHashSet( + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + ""); + + protected Set getPointsToNewExpr(PointsToSet pts) { + final Set allocSites = new HashSet<>(); + for (Iterator it = pts.iterator(); it.hasNext(); ) { + AllocNode n = it.next(); + allocSites.add(n.getNewExpr()); + } + return allocSites; + } + + private void init() { + ciAllocs = pag.getAllocNodes().size(); + csAllocs = pag.getAlloc().keySet().size(); + // globals + for (FieldSignature global : pag.getGlobalPointers()) { + try { + GlobalVarNode gvn = pag.findGlobalVarNode(global); + ClassType classType = global.getDeclClassType(); + if (PTAUtils.isFakeMainClass(classType)) { + continue; + } + Optional optClass = pta.getView().getClass(classType); + boolean app = optClass.get().isApplicationClass(); + totalGlobalPointers++; + if (app) { + appGlobalPointers++; + } + + PointsToSet pts = pta.reachingObjects(gvn); + final Set allocSites = getPointsToNewExpr(pts); + + totalGlobalPointsToCi += allocSites.size(); + totalGlobalPointsToCs += pts.size(); + if (app) { + appGlobalPointsToCi += allocSites.size(); + appGlobalPointsToCs += pts.size(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + // locals exclude Exceptions + for (Triple localTriple : pag.getLocalPointers()) { + try { + SootMethod method = localTriple.getFirst(); + Local local = localTriple.getSecond(); + Collection varNodes = pag.getVarNodes(method, local); + LocalVarNode lvn = pag.findLocalVarNode(method, local, localTriple.getThird()); + if (local.toString().contains("intermediate/")) { + continue; + } + mLocalVarNodes.add(lvn); + if (!handledNatives.contains(method.toString())) { + mLocalVarNodesNoNative.add(lvn); + } + boolean app = pta.getScene().isApplicationMethod(lvn.getMethod()); + totalLocalPointersCi++; + if (app) { + appLocalPointersCi++; + } + + totalLocalPointersCs += varNodes.size(); + if (app) { + appLocalPointersCs += varNodes.size(); + } + + PointsToSet pts = pta.reachingObjects(method, local); + final Set allocSites = getPointsToNewExpr(pts); + + totalLocalCiToCi += allocSites.size(); + totalLocalCiToCs += pts.size(); + if (app) { + appLocalCiToCi += allocSites.size(); + appLocalCiToCs += pts.size(); + } + + for (VarNode cvn : varNodes) { + PointsToSet cpts = pta.reachingObjects(cvn); + final Set callocSites = getPointsToNewExpr(cpts); + totalLocalCsToCi += callocSites.size(); + totalLocalCsToCs += cpts.size(); + if (app) { + appLocalCsToCi += callocSites.size(); + appLocalCsToCs += cpts.size(); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + // field points-to + for (ContextField cfvn : pag.getContextFields()) { + totalFieldPointsToCs += cfvn.getP2Set().size(); + } + + // stat context. + Map> mpag2contexts = pag.getMethod2ContextsMap(); + int[] cnts = new int[2]; + mpag2contexts.forEach( + (k, v) -> { + cnts[0]++; + cnts[1] += v.size(); + }); + contextCnt = cnts[1]; + avgCtxPerMthd = cnts[1] * 1.0 / cnts[0]; + + // stat method throw points-to. + for (SootMethod sm : pta.getNakedReachableMethods()) { + Node mThrow = pag.getMethodPAG(sm).nodeFactory().caseMethodThrow(); + PointsToSet pts = pta.reachingObjects(mThrow); + if (!pts.isEmpty()) { + methodThrowCnt++; + methodThrowPts.put(sm, pts); + } + } + + // stat avg pts. + for (SootMethod sm : pta.getNakedReachableMethods()) { + MethodPAG mpag = pag.getMethodPAG(sm); + MethodNodeFactory mnf = mpag.nodeFactory(); + if (!sm.isStatic()) { + mLocalVarNodes.add((LocalVarNode) mnf.caseThis()); + if (!handledNatives.contains(sm.toString())) { + mLocalVarNodesNoNative.add((LocalVarNode) mnf.caseThis()); + } + } + for (int i = 0; i < sm.getParameterCount(); ++i) { + Type mType = sm.getParameterType(i); + if (mType instanceof ReferenceType) { + mLocalVarNodes.add((LocalVarNode) mnf.caseParm(i)); + if (!handledNatives.contains(sm.toString())) { + mLocalVarNodesNoNative.add((LocalVarNode) mnf.caseParm(i)); + } + } + } + } + Set tmp = new HashSet<>(); + for (LocalVarNode lvn : mLocalVarNodes) { + SootMethod sm = lvn.getMethod(); + if (PTAUtils.isFakeMainMethod(sm)) { + tmp.add(lvn); + continue; + } + PointsToSet cpts = pta.reachingObjects(lvn); + final Set callocSites = getPointsToNewExpr(cpts); + if (callocSites.size() > 0) { + ptsCnt += callocSites.size(); + varCnt++; + if (!handledNatives.contains(sm.toString())) { + ptsCntNoNative += callocSites.size(); + varCntNoNative++; + } + } else { + tmp.add(lvn); + } + } + mLocalVarNodes.removeAll(tmp); + mLocalVarNodesNoNative.removeAll(tmp); + System.out.println("PTS relation:" + ptsCnt); + System.out.println("VAR CNT:" + varCnt); + System.out.println("AVG PTS: " + (ptsCnt * 1.0 / varCnt)); + System.out.println("PTS relation (no native):" + ptsCntNoNative); + System.out.println("VAR CNT (no native):" + varCntNoNative); + System.out.println("AVG PTS (no native): " + (ptsCntNoNative * 1.0 / varCntNoNative)); + } + + @Override + public void export(Exporter exporter) { + exporter.collectMetric("#Context:", String.valueOf(contextCnt)); + exporter.collectMetric("#Avg Context per Method:", String.valueOf(avgCtxPerMthd)); + exporter.collectMetric("#Method with Throw Pointer-to:", String.valueOf(methodThrowCnt)); + + exporter.collectMetric("#Alloc Node(CI): ", String.valueOf(ciAllocs)); + exporter.collectMetric("#Alloc Node(CS): ", String.valueOf(csAllocs)); + + exporter.collectMetric( + "#Global CS Pointer-to Relation:", String.valueOf(totalGlobalPointsToCs)); + exporter.collectMetric("#Local CS Pointer-to Relation:", String.valueOf(totalLocalCsToCs)); + exporter.collectMetric("#Field CS Pointer-to Relation:", String.valueOf(totalFieldPointsToCs)); + + exporter.collectMetric("#Global Pointer (lib + app):", String.valueOf(totalGlobalPointers)); + exporter.collectMetric( + "#Global Avg Points-To Target(CI):", + String.valueOf(((double) totalGlobalPointsToCi) / ((double) totalGlobalPointers))); + exporter.collectMetric( + "#Global Avg Points-To Target(CS):", + String.valueOf(((double) totalGlobalPointsToCs) / ((double) totalGlobalPointers))); + exporter.collectMetric("#App Global Pointer:", String.valueOf(appGlobalPointers)); + exporter.collectMetric( + "#App Global Avg Points-To Target(CI):", + String.valueOf(((double) appGlobalPointsToCi) / ((double) appGlobalPointers))); + exporter.collectMetric( + "#App Global Avg Points-To Target(CS):", + String.valueOf(((double) appGlobalPointsToCs) / ((double) appGlobalPointers))); + exporter.collectMetric( + "#Avg Points-to Target(CI):", String.valueOf(((double) ptsCnt) / (varCnt))); + exporter.collectMetric( + "#Avg Points-to Target without Native Var(CI):", + String.valueOf(((double) ptsCntNoNative) / (varCntNoNative))); + exporter.collectMetric("#Local Pointer (lib + app):", String.valueOf(totalLocalPointersCi)); + exporter.collectMetric( + "#Local Avg Points-To Target(CI):", + String.valueOf(((double) totalLocalCiToCi) / ((double) totalLocalPointersCi))); + exporter.collectMetric( + "#Local Avg Points-To Target(CS):", + String.valueOf(((double) totalLocalCiToCs) / ((double) totalLocalPointersCi))); + exporter.collectMetric("#App Local Pointer:", String.valueOf(appLocalPointersCi)); + exporter.collectMetric( + "#App Local Avg Points-To Target(CI):", + String.valueOf(((double) appLocalCiToCi) / ((double) appLocalPointersCi))); + exporter.collectMetric( + "#App Local Avg Points-To Target(CS):", + String.valueOf(((double) appLocalCiToCs) / ((double) appLocalPointersCi))); + exporter.collectMetric( + "#Context Local Pointer (lib + app):", String.valueOf(totalLocalPointersCs)); + exporter.collectMetric( + "#Context Local Avg Points-To Target(CI):", + String.valueOf(((double) totalLocalCsToCi) / ((double) totalLocalPointersCs))); + exporter.collectMetric( + "#Context Local Avg Points-To Target(CS):", + String.valueOf(((double) totalLocalCsToCs) / ((double) totalLocalPointersCs))); + exporter.collectMetric("#App Context Local Pointer:", String.valueOf(appLocalPointersCs)); + exporter.collectMetric( + "#App Context Local Avg Points-To Target(CI):", + String.valueOf(((double) appLocalCsToCi) / ((double) appLocalPointersCs))); + exporter.collectMetric( + "#App Context Local Avg Points-To Target(CS):", + String.valueOf(((double) appLocalCsToCs) / ((double) appLocalPointersCs))); + if (CoreConfig.v().getOutConfig().dumpStats) { + exporter.dumpMethodThrowPointsto(methodThrowPts); + exporter.dumpReachableLocalVars(mLocalVarNodes); + exporter.dumpReachableLocalVarsNoNative(mLocalVarNodesNoNative); + exporter.dumpInsensPointsTo(mLocalVarNodes, pta); + } + } +} diff --git a/sootup.qilin/src/main/java/qilin/stat/RuntimeStat.java b/sootup.qilin/src/main/java/qilin/stat/RuntimeStat.java new file mode 100644 index 00000000000..d4d3d9900db --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/stat/RuntimeStat.java @@ -0,0 +1,40 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.stat; + +import java.util.Date; + +public class RuntimeStat implements AbstractStat { + private Date startTime; + private long elapsedTime; + + public void begin() { + startTime = new Date(); + } + + public void end() { + Date endTime = new Date(); + elapsedTime = endTime.getTime() - startTime.getTime(); + } + + @Override + public void export(Exporter exporter) { + exporter.collectMetric("Time (sec):", String.valueOf(((double) elapsedTime) / 1000.0)); + } +} diff --git a/sootup.qilin/src/main/java/qilin/stat/SimplifiedEvaluator.java b/sootup.qilin/src/main/java/qilin/stat/SimplifiedEvaluator.java new file mode 100644 index 00000000000..1b76a5ca652 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/stat/SimplifiedEvaluator.java @@ -0,0 +1,220 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.stat; + +import com.google.common.collect.Sets; +import java.util.*; +import qilin.core.PTA; +import qilin.core.builder.FakeMainFactory; +import qilin.core.builder.MethodNodeFactory; +import qilin.core.builder.callgraph.Edge; +import qilin.core.builder.callgraph.OnFlyCallGraph; +import qilin.core.pag.*; +import qilin.core.sets.PointsToSet; +import qilin.util.PTAUtils; +import qilin.util.Stopwatch; +import qilin.util.Triple; +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.basic.Value; +import sootup.core.jimple.common.expr.AbstractInvokeExpr; +import sootup.core.jimple.common.expr.JCastExpr; +import sootup.core.jimple.common.expr.JStaticInvokeExpr; +import sootup.core.jimple.common.stmt.JAssignStmt; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.SootMethod; +import sootup.core.types.ReferenceType; +import sootup.core.types.Type; + +public class SimplifiedEvaluator implements IEvaluator { + protected final PTA pta; + protected final Exporter exporter; + protected Stopwatch stopwatch; + + public SimplifiedEvaluator(PTA pta) { + this.pta = pta; + exporter = new Exporter(pta); + } + + @Override + public void begin() { + stopwatch = Stopwatch.newAndStart("PTA evaluator"); + } + + @Override + public void end() { + stopwatch.stop(); + exporter.collectMetric("Time (sec):", String.valueOf(((double) stopwatch.elapsed()))); + exporter.collectMetric( + "#Reachable Method (CI):", String.valueOf(pta.getNakedReachableMethods().size() - 1)); + OnFlyCallGraph ciCallGraph = pta.getCallGraph(); + exporter.collectMetric( + "#Call Edge(CI):", String.valueOf(ciCallGraph.size() - FakeMainFactory.implicitCallEdges)); + + OnFlyCallGraph callGraph = pta.getCallGraph(); + + // loop over all reachable method's statement to find casts, local + // references, virtual call sites + Set reachableMethods = new HashSet<>(); + for (ContextMethod momc : pta.getCgb().getReachableMethods()) { + final SootMethod sm = momc.method(); + reachableMethods.add(sm); + } + int totalPolyCalls = 0; + int totalCastsMayFail = 0; + for (SootMethod sm : reachableMethods) { + // All the statements in the method + for (Stmt st : PTAUtils.getMethodBody(sm).getStmts()) { + // virtual calls + if (st.containsInvokeExpr()) { + AbstractInvokeExpr ie = st.getInvokeExpr(); + if (!(ie instanceof JStaticInvokeExpr)) { + // Virtual, Special or Instance + // have to check target soot method, cannot just + // count edges + Set targets = new HashSet<>(); + for (Iterator it = callGraph.edgesOutOf(st); it.hasNext(); ) + targets.add(it.next().tgt()); + if (targets.size() > 1) { + totalPolyCalls++; + } + } + } else if (st instanceof JAssignStmt) { + JAssignStmt assignStmt = (JAssignStmt) st; + Value rhs = assignStmt.getRightOp(); + Value lhs = assignStmt.getLeftOp(); + if (rhs instanceof JCastExpr && lhs.getType() instanceof ReferenceType) { + final Type targetType = rhs.getType(); + Value v = ((JCastExpr) rhs).getOp(); + if (!(v instanceof Local)) { + continue; + } + boolean fails = false; + Collection pts = pta.reachingObjects(sm, (Local) v).toCollection(); + for (Node n : pts) { + if (fails) { + break; + } + fails = !PTAUtils.castNeverFails(pta.getView(), n.getType(), targetType); + } + if (fails) { + totalCastsMayFail++; + } + } + } + } + } + AliasStat aliasStat = new AliasStat(pta); + aliasStat.aliasesProcessing(); + exporter.collectMetric("#May Fail Cast (Total):", String.valueOf(totalCastsMayFail)); + exporter.collectMetric("#Virtual Call Site(Polymorphic):", String.valueOf(totalPolyCalls)); + exporter.collectMetric( + "#globalAlias_incstst:", String.valueOf(aliasStat.getGlobalAliasesIncludingStSt())); + ptsStat(); + } + + private void ptsStat() { + int ptsCntNoNative = 0; + int varCntNoNative = 0; + PAG pag = pta.getPag(); + // locals exclude Exceptions + for (Triple localTriple : pag.getLocalPointers()) { + try { + SootMethod method = localTriple.getFirst(); + Local local = localTriple.getSecond(); + Type type = localTriple.getThird(); + LocalVarNode lvn = pag.findLocalVarNode(method, local, type); + if (local.toString().contains("intermediate/")) { + continue; + } + mLocalVarNodes.add(lvn); + } catch (Exception e) { + e.printStackTrace(); + } + } + + // stat avg pts. + for (SootMethod sm : pta.getNakedReachableMethods()) { + MethodPAG mpag = pag.getMethodPAG(sm); + MethodNodeFactory mnf = mpag.nodeFactory(); + if (!sm.isStatic()) { + mLocalVarNodes.add((LocalVarNode) mnf.caseThis()); + } + for (int i = 0; i < sm.getParameterCount(); ++i) { + Type mType = sm.getParameterType(i); + if (mType instanceof ReferenceType) { + mLocalVarNodes.add((LocalVarNode) mnf.caseParm(i)); + } + } + } + Set tmp = new HashSet<>(); + for (LocalVarNode lvn : mLocalVarNodes) { + SootMethod sm = lvn.getMethod(); + if (PTAUtils.isFakeMainMethod(sm)) { + tmp.add(lvn); + continue; + } + final Set callocSites = getPointsToNewExpr(pta.reachingObjects(lvn)); + if (callocSites.size() > 0) { + if (!handledNatives.contains(sm.toString())) { + ptsCntNoNative += callocSites.size(); + varCntNoNative++; + } + } else { + tmp.add(lvn); + } + } + mLocalVarNodes.removeAll(tmp); + + exporter.collectMetric( + "#Avg Points-to Target without Native Var(CI):", + String.valueOf(((double) ptsCntNoNative) / (varCntNoNative))); + } + + private final Set handledNatives = + Sets.newHashSet( + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + ""); + + private final Set mLocalVarNodes = new HashSet<>(); + + protected Set getPointsToNewExpr(PointsToSet pts) { + final Set allocSites = new HashSet<>(); + for (AllocNode n : pts.toCollection()) { + allocSites.add(n.getNewExpr()); + } + return allocSites; + } + + @Override + public String toString() { + return exporter.report(); + } +} diff --git a/sootup.qilin/src/main/java/qilin/stat/TypeClientStat.java b/sootup.qilin/src/main/java/qilin/stat/TypeClientStat.java new file mode 100644 index 00000000000..ae51e63f874 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/stat/TypeClientStat.java @@ -0,0 +1,174 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.stat; + +import java.util.*; +import qilin.CoreConfig; +import qilin.core.PTA; +import qilin.core.builder.FakeMainFactory; +import qilin.core.builder.callgraph.Edge; +import qilin.core.builder.callgraph.OnFlyCallGraph; +import qilin.core.pag.AllocNode; +import qilin.core.pag.ContextMethod; +import qilin.util.PTAUtils; +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.basic.Value; +import sootup.core.jimple.common.expr.AbstractInvokeExpr; +import sootup.core.jimple.common.expr.JCastExpr; +import sootup.core.jimple.common.expr.JStaticInvokeExpr; +import sootup.core.jimple.common.stmt.JAssignStmt; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.SootClass; +import sootup.core.model.SootMethod; +import sootup.core.types.ReferenceType; +import sootup.core.types.Type; +import sootup.core.views.View; + +public class TypeClientStat implements AbstractStat { + private final PTA pta; + + private int totalCasts = 0; + private int appCasts = 0; + private int totalCastsMayFail = 0; + private int appCastsMayFail = 0; + private int totalVirtualCalls = 0; + private int appVirtualCalls = 0; + private int totalPolyCalls = 0; + private int appPolyCalls = 0; + private int totalStaticCalls = 0; + private int totalPolyCallTargets = 0; + private int unreachable = 0; + private final Map polyCalls = new HashMap<>(); + private final Map> mayFailCasts = new HashMap<>(); + + public TypeClientStat(PTA pta) { + this.pta = pta; + init(); + } + + private void init() { + OnFlyCallGraph callGraph = pta.getCallGraph(); + + // loop over all reachable method's statement to find casts, local + // references, virtual call sites + Set reachableMethods = new HashSet<>(); + for (ContextMethod momc : pta.getCgb().getReachableMethods()) { + final SootMethod sm = momc.method(); + reachableMethods.add(sm); + } + + for (SootMethod sm : reachableMethods) { + View view = pta.getView(); + Optional osc = view.getClass(sm.getDeclaringClassType()); + if (!osc.isPresent()) { + continue; + } + SootClass sc = osc.get(); + boolean app = sc.isApplicationClass(); + + // All the statements in the method + for (Stmt st : PTAUtils.getMethodBody(sm).getStmts()) { + // virtual calls + if (st.containsInvokeExpr()) { + AbstractInvokeExpr ie = st.getInvokeExpr(); + if (ie instanceof JStaticInvokeExpr) { + totalStaticCalls++; + } else { // Virtual, Special or Instance + totalVirtualCalls++; + if (app) { + appVirtualCalls++; + } + // have to check target soot method, cannot just + // count edges + Set targets = new HashSet<>(); + + for (Iterator it = callGraph.edgesOutOf(st); it.hasNext(); ) + targets.add(it.next().tgt()); + if (targets.size() == 0) { + unreachable++; + } + if (targets.size() > 1) { + totalPolyCallTargets += targets.size(); + totalPolyCalls++; + polyCalls.put(ie, sm); + if (app) { + appPolyCalls++; + } + } + } + } else if (st instanceof JAssignStmt) { + JAssignStmt assignStmt = (JAssignStmt) st; + Value rhs = assignStmt.getRightOp(); + Value lhs = assignStmt.getLeftOp(); + if (rhs instanceof JCastExpr && lhs.getType() instanceof ReferenceType) { + final Type targetType = rhs.getType(); + Value v = ((JCastExpr) rhs).getOp(); + if (!(v instanceof Local)) { + continue; + } + totalCasts++; + if (app) { + appCasts++; + } + boolean fails = false; + Collection pts = pta.reachingObjects(sm, (Local) v).toCollection(); + for (AllocNode n : pts) { + if (fails) { + break; + } + fails = !PTAUtils.castNeverFails(pta.getView(), n.getType(), targetType); + } + + if (fails) { + totalCastsMayFail++; + mayFailCasts.computeIfAbsent(sm, k -> new HashSet<>()).add(st); + if (app) { + appCastsMayFail++; + } + } + } + } + } + } + } + + @Override + public void export(Exporter exporter) { + exporter.collectMetric("#Cast (Total):", String.valueOf(totalCasts)); + exporter.collectMetric("#Cast (AppOnly):", String.valueOf(appCasts)); + exporter.collectMetric("#May Fail Cast (Total):", String.valueOf(totalCastsMayFail)); + exporter.collectMetric("#May Fail Cast (AppOnly):", String.valueOf(appCastsMayFail)); + exporter.collectMetric( + "#Static Call Site(Total):", + String.valueOf(totalStaticCalls - FakeMainFactory.implicitCallEdges)); + exporter.collectMetric("#Virtual Call Site(Total):", String.valueOf(totalVirtualCalls)); + exporter.collectMetric("#Virtual Call Site(AppOnly):", String.valueOf(appVirtualCalls)); + exporter.collectMetric("#Virtual Call Site(Polymorphic):", String.valueOf(totalPolyCalls)); + exporter.collectMetric( + "#Virtual Call Site(Polymorphic AppOnly):", String.valueOf(appPolyCalls)); + exporter.collectMetric("#Virtual Call Site(Unreachable):", String.valueOf(unreachable)); + exporter.collectMetric( + "#Avg Poly Call Targets:", String.valueOf(1.0 * totalPolyCallTargets / totalPolyCalls)); + + if (CoreConfig.v().getOutConfig().dumpStats) { + exporter.dumpPolyCalls(polyCalls); + exporter.dumpMayFailCasts(mayFailCasts); + } + } +} diff --git a/sootup.qilin/src/main/java/qilin/stat/YummyStat.java b/sootup.qilin/src/main/java/qilin/stat/YummyStat.java new file mode 100644 index 00000000000..771b7cc319c --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/stat/YummyStat.java @@ -0,0 +1,104 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.stat; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import qilin.CoreConfig; +import qilin.core.PTA; +import qilin.core.builder.MethodNodeFactory; +import qilin.core.builder.callgraph.Edge; +import qilin.core.builder.callgraph.OnFlyCallGraph; +import qilin.core.pag.ContextMethod; +import qilin.core.sets.PointsToSet; +import qilin.util.PTAUtils; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.SootMethod; + +public class YummyStat implements AbstractStat { + private final PTA pta; + private long singleCallCnt; + private long singleReceiverCnt; + private long singleCallSingleReceiverCnt; + private final Set singleCalls = new HashSet<>(); + private final Set singleReceivers = new HashSet<>(); + private final Set singleCallSingleReceivers = new HashSet<>(); + + public YummyStat(PTA pta) { + this.pta = pta; + init(); + } + + private void init() { + Map> target2callsites = new HashMap<>(); + OnFlyCallGraph ciCallGraph = pta.getCallGraph(); + for (Edge edge : ciCallGraph) { + Stmt callUnit = edge.srcUnit(); + SootMethod target = edge.tgt(); + if (callUnit != null && target != null) { + target2callsites.computeIfAbsent(target, k -> new HashSet<>()).add(callUnit); + } + } + + target2callsites.entrySet().stream() + .filter(e -> e.getValue().size() == 1) + .map(Map.Entry::getKey) + .filter(m -> !m.isStatic()) + .forEach(singleCalls::add); + + singleCallCnt = singleCalls.size(); + + Set instanceReachables = new HashSet<>(); + for (final ContextMethod momc : pta.getReachableMethods()) { + SootMethod method = momc.method(); + if (PTAUtils.hasBody(method) && !method.isStatic()) { + instanceReachables.add(method); + } + } + + for (SootMethod method : instanceReachables) { + MethodNodeFactory nf = pta.getPag().getMethodPAG(method).nodeFactory(); + PointsToSet pts = pta.reachingObjects(nf.caseThis()).toCIPointsToSet(); + int ptSize = pts.size(); + if (ptSize == 1) { + singleReceivers.add(method); + } + } + + singleReceiverCnt = singleReceivers.size(); + + singleCalls.stream().filter(singleReceivers::contains).forEach(singleCallSingleReceivers::add); + singleCallSingleReceiverCnt = singleCallSingleReceivers.size(); + } + + @Override + public void export(Exporter exporter) { + exporter.collectMetric("#Single-Call Methods:", String.valueOf(singleCallCnt)); + exporter.collectMetric("#Single-Receiver Methods:", String.valueOf(singleReceiverCnt)); + exporter.collectMetric( + "#Single-Call-Single-Receiver Methods:", String.valueOf(singleCallSingleReceiverCnt)); + if (CoreConfig.v().getOutConfig().dumpStats) { + exporter.dumpSingleCallMethods(singleCalls); + exporter.dumpSingleReceiverMethods(singleReceivers); + exporter.dumpSingleCallSingleReceiverMethods(singleCallSingleReceivers); + } + } +} diff --git a/sootup.qilin/src/main/java/qilin/util/ANSIColor.java b/sootup.qilin/src/main/java/qilin/util/ANSIColor.java new file mode 100644 index 00000000000..3ea03db8a2d --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/ANSIColor.java @@ -0,0 +1,37 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.util; + +public class ANSIColor { + public static final String RESET = "\u001b[0m"; + public static final String BOLD = "\u001b[1m"; + public static final String BLACK = "\u001b[30m"; + public static final String RED = "\u001b[31m"; + public static final String GREEN = "\u001b[32m"; + public static final String YELLOW = "\u001b[33m"; + public static final String BLUE = "\u001b[34m"; + public static final String PURPLE = "\u001b[35m"; + public static final String CYAN = "\u001b[36m"; + public static final String WHITE = "\u001b[37m"; + public static final String BG_BLACK = "\u001b[40m"; + + public static String color(final String color, final String s) { + return color + s + RESET; + } +} diff --git a/sootup.qilin/src/main/java/qilin/util/ArrayNumberer.java b/sootup.qilin/src/main/java/qilin/util/ArrayNumberer.java new file mode 100644 index 00000000000..58b4cee1b1b --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/ArrayNumberer.java @@ -0,0 +1,154 @@ +package qilin.util; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 2002 Ondrej Lhotak + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import java.util.Arrays; +import java.util.BitSet; +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * A class that numbers objects, so they can be placed in bitsets. + * + * @author Ondrej Lhotak + * @author xiao, generalize it. + */ +public class ArrayNumberer implements IterableNumberer { + + protected E[] numberToObj; + protected int lastNumber; + protected BitSet freeNumbers; + + @SuppressWarnings("unchecked") + public ArrayNumberer() { + this.numberToObj = (E[]) new Numberable[1024]; + this.lastNumber = 0; + } + + public ArrayNumberer(E[] elements) { + this.numberToObj = elements; + this.lastNumber = elements.length; + } + + private void resize(int n) { + numberToObj = Arrays.copyOf(numberToObj, n); + } + + @Override + public synchronized void add(E o) { + if (o.getNumber() != 0) { + return; + } + + // In case we removed entries from the numberer, we want to re-use the free space + int chosenNumber = -1; + if (freeNumbers != null) { + int ns = freeNumbers.nextSetBit(0); + if (ns != -1) { + chosenNumber = ns; + freeNumbers.clear(ns); + } + } + if (chosenNumber == -1) { + chosenNumber = ++lastNumber; + } + if (chosenNumber >= numberToObj.length) { + resize(numberToObj.length * 2); + } + numberToObj[chosenNumber] = o; + o.setNumber(chosenNumber); + } + + @Override + public long get(E o) { + if (o == null) { + return 0; + } + int ret = o.getNumber(); + if (ret == 0) { + throw new RuntimeException("unnumbered: " + o); + } + return ret; + } + + @Override + public E get(long number) { + if (number == 0) { + return null; + } + E ret = numberToObj[(int) number]; + if (ret == null) { + return null; + } + return ret; + } + + @Override + public int size() { + return lastNumber; + } + + @Override + public Iterator iterator() { + return new Iterator() { + int cur = 1; + + @Override + public final boolean hasNext() { + return cur <= lastNumber && cur < numberToObj.length && numberToObj[cur] != null; + } + + @Override + public final E next() { + if (hasNext()) { + return numberToObj[cur++]; + } + throw new NoSuchElementException(); + } + + @Override + public final void remove() { + ArrayNumberer.this.remove(numberToObj[cur - 1]); + } + }; + } + + @Override + public boolean remove(E o) { + if (o == null) { + return false; + } + + int num = o.getNumber(); + if (num == 0) { + return false; + } + if (freeNumbers == null) { + freeNumbers = new BitSet(2 * num); + } + numberToObj[num] = null; + o.setNumber(0); + freeNumbers.set(num); + return true; + } +} diff --git a/sootup.qilin/src/main/java/qilin/util/BitSetIterator.java b/sootup.qilin/src/main/java/qilin/util/BitSetIterator.java new file mode 100644 index 00000000000..2029dcb4181 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/BitSetIterator.java @@ -0,0 +1,92 @@ +package qilin.util; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 2001 Felix Kwok + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import java.util.NoSuchElementException; + +/** + * A fast enumerator for sparse bit sets. When the enumerator is created, it takes a snapshot of the + * underlying BitVector, and iterates through the set bits. Note that this class almost implements + * the Iterator interface, but it doesn't because the return type of next is int rather than Object. + */ +public class BitSetIterator { + + long[] bits; // Bits inherited from the underlying BitVector + int index; // The 64-bit block currently being examined + long save = 0; // A copy of the 64-bit block (for fast access) + + /* + * Computes log_2(x) modulo 67. This uses the fact that 2 is a primitive root modulo 67 + */ + static final int[] lookup = { + -1, 0, 1, 39, 2, 15, 40, 23, 3, 12, 16, 59, 41, 19, 24, 54, 4, -1, 13, 10, 17, 62, 60, 28, 42, + 30, 20, 51, 25, 44, 55, 47, 5, 32, -1, 38, 14, 22, 11, 58, 18, 53, -1, 9, 61, 27, 29, 50, 43, + 46, 31, 37, 21, 57, 52, 8, 26, 49, 45, 36, 56, 7, 48, 35, 6, 34, 33 + }; + + /** Creates a new BitSetIterator */ + BitSetIterator(long[] bits) { + // this.bits = new long[bits.length]; + // System.arraycopy(bits,0,this.bits,0,bits.length); + this.bits = bits; + index = 0; + + /* Zip through empty blocks */ + while (index < bits.length && bits[index] == 0L) { + index++; + } + if (index < bits.length) { + save = bits[index]; + } + } + + /** Returns true if there are more set bits in the BitVector; false otherwise. */ + public boolean hasNext() { + return index < bits.length; + } + + /** Returns the index of the next set bit. Note that the return type is int, and not Object. */ + public int next() { + if (index >= bits.length) { + throw new NoSuchElementException(); + } + + long k = (save & (save - 1)); // Clears the last non-zero bit. save is guaranteed non-zero. + long diff = save ^ k; // Finds out which bit it is. diff has exactly one bit set. + save = k; + + // Computes the position of the set bit. + int result = (diff < 0) ? 64 * index + 63 : 64 * index + lookup[(int) (diff % 67)]; + + if (save == 0) { + index++; + while (index < bits.length && bits[index] == 0L) { + index++; + } + if (index < bits.length) { + save = bits[index]; + } + } + return result; + } +} diff --git a/sootup.qilin/src/main/java/qilin/util/BitVector.java b/sootup.qilin/src/main/java/qilin/util/BitVector.java new file mode 100644 index 00000000000..25fa136250f --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/BitVector.java @@ -0,0 +1,464 @@ +package qilin.util; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 2003 Ondrej Lhotak + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +/** + * This is the Soot internal implementation of java.util.BitSet with Felix and Jerome's clever + * efficient iterator. It was re-implemented from scratch by Ondrej Lhotak to avoid licence issues. + * It was named BitVector rather than BitSet to avoid a name clash with the one in the standard Java + * library. + * + * @author Ondrej Lhotak + */ +public class BitVector { + + private long[] bits; + + public BitVector() { + this(64); + } + + /** Copy constructor */ + // Added by Adam Richard. More efficient than clone(), and easier to extend + public BitVector(BitVector other) { + final long[] otherBits = other.bits; + bits = new long[otherBits.length]; + System.arraycopy(otherBits, 0, bits, 0, otherBits.length); + } + + public BitVector(int numBits) { + int lastIndex = indexOf(numBits - 1); + bits = new long[lastIndex + 1]; + } + + private int indexOf(int bit) { + return bit >> 6; + } + + private long mask(int bit) { + return 1L << (bit & 63); + } + + public void and(BitVector other) { + if (this == other) { + return; + } + final long[] otherBits = other.bits; + int numToAnd = otherBits.length; + if (bits.length < numToAnd) { + numToAnd = bits.length; + } + int i; + for (i = 0; i < numToAnd; i++) { + bits[i] = bits[i] & otherBits[i]; + } + for (; i < bits.length; i++) { + bits[i] = 0L; + } + } + + public void andNot(BitVector other) { + final long[] otherBits = other.bits; + int numToAnd = otherBits.length; + if (bits.length < numToAnd) { + numToAnd = bits.length; + } + for (int i = 0; i < numToAnd; i++) { + bits[i] = bits[i] & ~otherBits[i]; + } + } + + public void clear(int bit) { + if (indexOf(bit) < bits.length) { + bits[indexOf(bit)] &= ~mask(bit); + } + } + + @Override + public Object clone() { + try { + BitVector ret = (BitVector) super.clone(); + System.arraycopy(bits, 0, ret.bits, 0, ret.bits.length); + return ret; + } catch (CloneNotSupportedException e) { + // cannot occur + throw new RuntimeException(e); + } + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof BitVector)) { + return false; + } + final long[] otherBits = ((BitVector) o).bits; + long[] longer = otherBits; + int min = bits.length; + if (otherBits.length < min) { + min = otherBits.length; + longer = bits; + } + int i; + for (i = 0; i < min; i++) { + if (bits[i] != otherBits[i]) { + return false; + } + } + for (; i < longer.length; i++) { + if (longer[i] != 0L) { + return false; + } + } + return true; + } + + public boolean get(int bit) { + if (indexOf(bit) >= bits.length) { + return false; + } + return (bits[indexOf(bit)] & mask(bit)) != 0L; + } + + @Override + public int hashCode() { + long ret = 0; + for (long element : bits) { + ret ^= element; + } + return (int) ((ret >> 32) ^ ret); + } + + /** Returns index of highest-numbered one bit. */ + public int length() { + int i; + for (i = bits.length - 1; i >= 0; i--) { + if (bits[i] != 0L) { + break; + } + } + if (i < 0) { + return 0; + } + long j = bits[i]; + i++; + i <<= 6; + for (long k = 1L << 63; (k & j) == 0L; k >>= 1, i--) {} + return i; + } + + public void copyFrom(BitVector other) { + if (this == other) { + return; + } + final long[] otherBits = other.bits; + int j; + for (j = otherBits.length - 1; j >= 0; j--) { + if (otherBits[j] != 0L) { + break; + } + } + expand(j << 6); + int i = j + 1; + for (; j >= 0; j--) { + bits[j] = otherBits[j]; + } + for (; i < bits.length; i++) { + bits[i] = 0L; + } + } + + public void or(BitVector other) { + if (this == other) { + return; + } + final long[] otherBits = other.bits; + int j; + for (j = otherBits.length - 1; j >= 0; j--) { + if (otherBits[j] != 0L) { + break; + } + } + expand(j << 6); + for (; j >= 0; j--) { + bits[j] |= otherBits[j]; + } + } + + /** + * Count the number of ones in the bitvector. + * + * @author Adam Richard This is Brian Kernighan's algorithm from: + * http://graphics.stanford.edu/~seander/bithacks.html and is efficient for sparse bit sets. + */ + public int cardinality() { + int c = 0; + for (long v : bits) { + while (v != 0) { + v &= v - 1; + ++c; + } + } + return c; + } + + /** + * Returns true if the both the current and the specified bitvectors have at least one bit set in + * common. + * + * @author Quentin Sabah Inspired by the BitVector.and method. + */ + public boolean intersects(BitVector other) { + final long[] otherBits = other.bits; + int numToCheck = otherBits.length; + if (bits.length < numToCheck) { + numToCheck = bits.length; + } + int i; + for (i = 0; i < numToCheck; i++) { + if ((bits[i] & otherBits[i]) != 0) { + return true; + } + } + return false; + } + + private void expand(int bit) { + int n = indexOf(bit) + 1; + if (n <= bits.length) { + return; + } + if (bits.length * 2 > n) { + n = bits.length * 2; + } + long[] newBits = new long[n]; + System.arraycopy(bits, 0, newBits, 0, bits.length); + bits = newBits; + } + + public void xor(BitVector other) { + if (this == other) { + return; + } + final long[] otherBits = other.bits; + int j; + for (j = otherBits.length - 1; j >= 0; j--) { + if (otherBits[j] != 0L) { + break; + } + } + expand(j << 6); + for (; j >= 0; j--) { + bits[j] ^= otherBits[j]; + } + } + + public boolean set(int bit) { + expand(bit); + boolean ret = !get(bit); + bits[indexOf(bit)] |= mask(bit); + return ret; + } + + /** Returns number of bits in the underlying array. */ + public int size() { + return bits.length << 6; + } + + @Override + public String toString() { + StringBuilder ret = new StringBuilder(); + ret.append('{'); + boolean start = true; + for (BitSetIterator it = new BitSetIterator(bits); it.hasNext(); ) { + int bit = it.next(); + if (start) { + start = false; + } else { + ret.append(", "); + } + ret.append(bit); + } + ret.append('}'); + return ret.toString(); + } + + /** + * Computes this = this OR ((orset AND andset ) AND (NOT andnotset)) Returns true iff this is + * modified. + */ + public boolean orAndAndNot(BitVector orset, BitVector andset, BitVector andnotset) { + final long[] a, b, c, d; + final int al, bl, cl, dl; + { + a = this.bits; + al = a.length; + } + if (orset == null) { + b = null; + bl = 0; + } else { + b = orset.bits; + bl = b.length; + } + if (andset == null) { + c = null; + cl = 0; + } else { + c = andset.bits; + cl = c.length; + } + if (andnotset == null) { + d = null; + dl = 0; + } else { + d = andnotset.bits; + dl = d.length; + } + + final long[] e; + if (al < bl) { + e = new long[bl]; + System.arraycopy(a, 0, e, 0, al); + this.bits = e; + } else { + e = a; + } + + boolean ret = false; + if (c == null) { + if (dl <= bl) { + int i = 0; + for (; i < dl; i++) { + long l = b[i] & ~d[i]; + if ((l & ~e[i]) != 0) { + ret = true; + } + e[i] |= l; + } + for (; i < bl; i++) { + long l = b[i]; + if ((l & ~e[i]) != 0) { + ret = true; + } + e[i] |= l; + } + } else { + for (int i = 0; i < bl; i++) { + long l = b[i] & ~d[i]; + if ((l & ~e[i]) != 0) { + ret = true; + } + e[i] |= l; + } + } + } else if (bl <= cl && bl <= dl) { + // bl is the shortest + for (int i = 0; i < bl; i++) { + long l = b[i] & c[i] & ~d[i]; + if ((l & ~e[i]) != 0) { + ret = true; + } + e[i] |= l; + } + } else if (cl <= bl && cl <= dl) { + // cl is the shortest + for (int i = 0; i < cl; i++) { + long l = b[i] & c[i] & ~d[i]; + if ((l & ~e[i]) != 0) { + ret = true; + } + e[i] |= l; + } + } else { + // dl is the shortest + int i = 0; + for (; i < dl; i++) { + long l = b[i] & c[i] & ~d[i]; + if ((l & ~e[i]) != 0) { + ret = true; + } + e[i] |= l; + } + for (int shorter = (bl < cl) ? bl : cl; i < shorter; i++) { + long l = b[i] & c[i]; + if ((l & ~e[i]) != 0) { + ret = true; + } + e[i] |= l; + } + } + + return ret; + } + + public static BitVector and(BitVector set1, BitVector set2) { + int min = set1.size(); + { + int max = set2.size(); + if (min > max) { + min = max; + } + // max is not necessarily correct at this point, so let it go + // out of scope + } + + BitVector ret = new BitVector(min); + long[] retbits = ret.bits; + long[] bits1 = set1.bits; + long[] bits2 = set2.bits; + min >>= 6; + for (int i = 0; i < min; i++) { + retbits[i] = bits1[i] & bits2[i]; + } + return ret; + } + + public static BitVector or(BitVector set1, BitVector set2) { + int min = set1.size(); + int max = set2.size(); + if (min > max) { + min = max; + max = set1.size(); + } + + BitVector ret = new BitVector(max); + long[] retbits = ret.bits; + long[] bits1 = set1.bits; + long[] bits2 = set2.bits; + min >>= 6; + max >>= 6; + for (int i = 0; i < min; i++) { + retbits[i] = bits1[i] | bits2[i]; + } + if (bits1.length == min) { + System.arraycopy(bits2, min, retbits, min, max - min); + } else { + System.arraycopy(bits1, min, retbits, min, max - min); + } + return ret; + } + + public BitSetIterator iterator() { + return new BitSetIterator(bits); + } +} diff --git a/sootup.qilin/src/main/java/qilin/util/Chain.java b/sootup.qilin/src/main/java/qilin/util/Chain.java new file mode 100644 index 00000000000..6f73017548f --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/Chain.java @@ -0,0 +1,134 @@ +package qilin.util; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 1999 Patrice Pominville + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import java.io.Serializable; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +/** + * Augmented data type guaranteeing O(1) insertion and removal from a set of ordered, unique + * elements. + * + * @param element type + */ +public interface Chain extends Collection, Serializable { + + /** Inserts toInsert in the Chain before point. */ + public void insertBefore(E toInsert, E point); + + /** Inserts toInsert in the Chain after point. */ + public void insertAfter(E toInsert, E point); + + /** Inserts toInsert in the Chain before point. */ + public void insertBefore(Chain toInsert, E point); + + /** Inserts toInsert in the Chain after point. */ + public void insertAfter(Chain toInsert, E point); + + /** Inserts toInsert in the Chain before point. */ + public void insertBefore(List toInsert, E point); + + /** Inserts toInsert in the Chain after point. */ + public void insertAfter(List toInsert, E point); + + /** Inserts toInsert in the Chain before point. */ + public void insertBefore(Collection toInsert, E point); + + /** Inserts toInsert in the Chain after point. */ + public void insertAfter(Collection toInsert, E point); + + /** Replaces out in the Chain by in. */ + public void swapWith(E out, E in); + + /** + * Removes the given object from this Chain. Parameter has to be of type {@link Object} to be + * compatible with the {@link Collection} interface. + */ + @Override + public boolean remove(Object u); + + /** Adds the given object at the beginning of the Chain. */ + public void addFirst(E u); + + /** Adds the given object at the end of the Chain. */ + public void addLast(E u); + + /** Removes the first object contained in this Chain. */ + public void removeFirst(); + + /** Removes the last object contained in this Chain. */ + public void removeLast(); + + /** + * Returns true if object someObject follows object someReferenceObject + * in the Chain, i.e. someReferenceObject comes first and then someObject. + */ + public boolean follows(E someObject, E someReferenceObject); + + /** Returns the first object in this Chain. */ + public E getFirst(); + + /** Returns the last object in this Chain. */ + public E getLast(); + + /** Returns the object immediately following point. */ + public E getSuccOf(E point); + + /** Returns the object immediately preceding point. */ + public E getPredOf(E point); + + /** + * Returns an iterator over a copy of this chain. This avoids ConcurrentModificationExceptions + * from being thrown if the underlying Chain is modified during iteration. Do not use this to + * remove elements which have not yet been iterated over! + */ + public Iterator snapshotIterator(); + + /** Returns an iterator over this Chain. */ + @Override + public Iterator iterator(); + + /** Returns an iterator over this Chain, starting at the given object. */ + public Iterator iterator(E u); + + /** Returns an iterator over this Chain, starting at head and reaching tail (inclusive). */ + public Iterator iterator(E head, E tail); + + /** Returns the size of this Chain. */ + @Override + public int size(); + + /** Returns the number of times this chain has been modified. */ + long getModificationCount(); + + /** + * Gets all elements in the chain. There is no guarantee on sorting. On the other hand, the + * collection returned by this method is thread-safe. You can iterate over it even in the case of + * concurrent modifications to the underlying chain. + * + * @return All elements in the chain in an unsorted collection + */ + public Collection getElementsUnsorted(); +} diff --git a/sootup.qilin/src/main/java/qilin/util/DataFactory.java b/sootup.qilin/src/main/java/qilin/util/DataFactory.java new file mode 100644 index 00000000000..57e6342e228 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/DataFactory.java @@ -0,0 +1,48 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.util; + +import java.util.*; + +public class DataFactory { + public static List createList() { + return new ArrayList<>(); + // return new CopyOnWriteArrayList<>(); + } + + public static Set createSet() { + return new HashSet<>(); + // return ConcurrentHashMap.newKeySet(); + } + + public static Set createSet(int initCapacity) { + return new HashSet<>(initCapacity); + // return ConcurrentHashMap.newKeySet(initCapacity); + } + + public static Map createMap() { + return new HashMap<>(); + // return new ConcurrentHashMap<>(); + } + + public static Map createMap(int initCapacity) { + return new HashMap<>(initCapacity); + // return new ConcurrentHashMap<>(initCapacity); + } +} diff --git a/sootup.qilin/src/main/java/qilin/util/Invalidable.java b/sootup.qilin/src/main/java/qilin/util/Invalidable.java new file mode 100644 index 00000000000..00102d7836f --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/Invalidable.java @@ -0,0 +1,41 @@ +package qilin.util; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 2005 Ondrej Lhotak + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +/** + * A class implementing this interface can be invalidated. The invalidation state can be retrieved + * by other classes. + * + * @author Marc Miltenberger + */ +public interface Invalidable { + /** + * Return true if the object is invalid. + * + * @return true if the object is invalid. + */ + public boolean isInvalid(); + + /** Invalidates the object. Does nothing if the object is already invalid. */ + public void invalidate(); +} diff --git a/sootup.qilin/src/main/java/qilin/util/IterableNumberer.java b/sootup.qilin/src/main/java/qilin/util/IterableNumberer.java new file mode 100644 index 00000000000..fbc649e88ea --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/IterableNumberer.java @@ -0,0 +1,36 @@ +package qilin.util; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 2005 Ondrej Lhotak + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import java.util.Iterator; + +/** + * A numberer which also supports an iterator on newly-added objects. + * + * @author xiao, generalize the interface + */ +public interface IterableNumberer extends Numberer, Iterable { + /** Returns an iterator over all objects added to the numberer. */ + @Override + Iterator iterator(); +} diff --git a/sootup.qilin/src/main/java/qilin/util/MemoryWatcher.java b/sootup.qilin/src/main/java/qilin/util/MemoryWatcher.java new file mode 100644 index 00000000000..20a5daf57bd --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/MemoryWatcher.java @@ -0,0 +1,79 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.util; + +import java.util.Timer; +import java.util.TimerTask; +import oshi.SystemInfo; +import oshi.software.os.OSProcess; +import oshi.software.os.OperatingSystem; + +public class MemoryWatcher extends Timer { + private final long pid; + private final String name; + private final long[] maxMemory; + + public MemoryWatcher(long pid, String name) { + this.pid = pid; + this.name = name; + this.maxMemory = new long[1]; + } + + private TimerTask task; + + public void start() { + this.maxMemory[0] = 0; + task = + new TimerTask() { + @Override + public void run() { + SystemInfo si = new SystemInfo(); + OperatingSystem os = si.getOperatingSystem(); + OSProcess process = os.getProcess((int) pid); + long mem = process.getResidentSetSize(); + if (mem > maxMemory[0]) { + maxMemory[0] = mem; + } + } + }; + schedule(task, 0, 100); + } + + public void stop() { + task.cancel(); + this.cancel(); + } + + public double inKiloByte() { + return maxMemory[0] / 1024.0; + } + + public double inMegaByte() { + return maxMemory[0] / (1024.0 * 1024.0); + } + + public double inGigaByte() { + return maxMemory[0] / (1024.0 * 1024.0 * 1024.0); + } + + @Override + public String toString() { + return String.format("%s consumed memory: %.2f MB", this.name, this.inMegaByte()); + } +} diff --git a/sootup.qilin/src/main/java/qilin/util/Numberable.java b/sootup.qilin/src/main/java/qilin/util/Numberable.java new file mode 100644 index 00000000000..f8a1d83e64d --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/Numberable.java @@ -0,0 +1,34 @@ +package qilin.util; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 2002 Ondrej Lhotak + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +/** + * A class that numbers objects, so they can be placed in bitsets. + * + * @author Ondrej Lhotak + */ +public interface Numberable { + public void setNumber(int number); + + public int getNumber(); +} diff --git a/sootup.qilin/src/main/java/qilin/util/NumberedString.java b/sootup.qilin/src/main/java/qilin/util/NumberedString.java new file mode 100644 index 00000000000..02e1c9dcbfd --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/NumberedString.java @@ -0,0 +1,88 @@ +package qilin.util; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 2002 Ondrej Lhotak + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +/** + * A class that assigns integers to java.lang.Strings. + * + * @author Ondrej Lhotak + */ +public final class NumberedString implements Numberable { + + private final String s; + private volatile int number; + + public NumberedString(String s) { + this.s = s; + } + + @Override + public final void setNumber(int number) { + this.number = number; + } + + @Override + public final int getNumber() { + return number; + } + + @Override + public final String toString() { + return getString(); + } + + public final String getString() { + if (number == 0) { + throw new RuntimeException("oops"); + } + return s; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + number; + result = prime * result + ((s == null) ? 0 : s.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || this.getClass() != obj.getClass()) { + return false; + } + NumberedString other = (NumberedString) obj; + if (this.number != other.number) { + return false; + } + if (this.s == null) { + return other.s == null; + } else { + return this.s.equals(other.s); + } + } +} diff --git a/sootup.qilin/src/main/java/qilin/util/Numberer.java b/sootup.qilin/src/main/java/qilin/util/Numberer.java new file mode 100644 index 00000000000..cb4ba93dd7c --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/Numberer.java @@ -0,0 +1,53 @@ +package qilin.util; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 2004 Ondrej Lhotak + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +/** + * A numberer converts objects to unique non-negative integers, and vice-versa. + * + * @author xiao, generalize the interface + */ +public interface Numberer { + /** Tells the numberer that a new object needs to be assigned a number. */ + public void add(E o); + + /** + * Removes the number for a given object. + * + * @param o the element + * @return true if the removal was successful, false when not + */ + public boolean remove(E o); + + /** + * Should return the number that was assigned to object o that was previously passed as an + * argument to add(). + */ + public long get(E o); + + /** Should return the object that was assigned the number. */ + public E get(long number); + + /** Should return the number of objects that have been assigned numbers. */ + public int size(); +} diff --git a/sootup.qilin/src/main/java/qilin/util/PTAUtils.java b/sootup.qilin/src/main/java/qilin/util/PTAUtils.java new file mode 100644 index 00000000000..d5392c5775c --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/PTAUtils.java @@ -0,0 +1,464 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.util; + +import java.io.*; +import java.net.URL; +import java.util.*; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.stream.Collectors; +import org.apache.commons.io.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import qilin.core.PTA; +import qilin.core.PTAScene; +import qilin.core.builder.MethodNodeFactory; +import qilin.core.builder.callgraph.Edge; +import qilin.core.context.Context; +import qilin.core.context.ContextElement; +import qilin.core.context.ContextElements; +import qilin.core.pag.*; +import qilin.core.sets.PointsToSet; +import qilin.pta.PTAConfig; +import sootup.core.inputlocation.AnalysisInputLocation; +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.basic.Value; +import sootup.core.jimple.common.constant.IntConstant; +import sootup.core.jimple.common.expr.AbstractInstanceInvokeExpr; +import sootup.core.jimple.common.expr.AbstractInvokeExpr; +import sootup.core.jimple.common.expr.JNewArrayExpr; +import sootup.core.jimple.common.expr.JStaticInvokeExpr; +import sootup.core.jimple.common.stmt.JAssignStmt; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.Body; +import sootup.core.model.SootClass; +import sootup.core.model.SootMethod; +import sootup.core.model.SourceType; +import sootup.core.signatures.PackageName; +import sootup.core.types.ArrayType; +import sootup.core.types.ClassType; +import sootup.core.types.NullType; +import sootup.core.types.PrimitiveType; +import sootup.core.types.Type; +import sootup.core.util.printer.JimplePrinter; +import sootup.core.views.View; +import sootup.java.bytecode.inputlocation.JavaClassPathAnalysisInputLocation; +import sootup.java.core.JavaIdentifierFactory; +import sootup.java.core.views.JavaView; + +public final class PTAUtils { + private static final Logger logger = LoggerFactory.getLogger(PTAUtils.class); + + public static ClassType getClassType(String fullyQualifiedClassName) { + return JavaIdentifierFactory.getInstance().getClassType(fullyQualifiedClassName); + } + + public static boolean isStaticInitializer(SootMethod method) { + return method.getName().equals(""); + } + + public static boolean isConstructor(SootMethod method) { + return method.getName().equals(""); + } + + public static Map> calcStaticThisPTS(PTA pta) { + Map> pts = new HashMap<>(); + Set workList = new HashSet<>(); + PAG pag = pta.getPag(); + // add all instance methods which potentially contain static call + for (SootMethod method : pta.getNakedReachableMethods()) { + if (PTAUtils.isFakeMainMethod(method) || hasBody(method) && !method.isStatic()) { + MethodPAG srcmpag = pag.getMethodPAG(method); + LocalVarNode thisRef = (LocalVarNode) srcmpag.nodeFactory().caseThis(); + final PointsToSet other = pta.reachingObjects(thisRef).toCIPointsToSet(); + + for (final Stmt s : srcmpag.getInvokeStmts()) { + AbstractInvokeExpr ie = s.getInvokeExpr(); + if (ie instanceof JStaticInvokeExpr) { + for (Iterator it = pta.getCallGraph().edgesOutOf(s); it.hasNext(); ) { + Edge e = it.next(); + SootMethod tgtmtd = e.tgt(); + MethodPAG tgtmpag = pag.getMethodPAG(tgtmtd); + MethodNodeFactory tgtnf = tgtmpag.nodeFactory(); + LocalVarNode tgtThisRef = (LocalVarNode) tgtnf.caseThis(); + // create "THIS" ptr for static method + Set addTo = pts.computeIfAbsent(tgtThisRef, k -> new HashSet<>()); + boolean returnValue = false; + for (Iterator itx = other.iterator(); itx.hasNext(); ) { + AllocNode node = itx.next(); + if (addTo.add(node)) { + returnValue = true; + } + } + if (returnValue) { + workList.add(tgtmtd); + } + } + } + } + } + } + while (!workList.isEmpty()) { + SootMethod method = workList.iterator().next(); + workList.remove(method); + MethodPAG srcmpag = pag.getMethodPAG(method); + LocalVarNode thisRef = (LocalVarNode) srcmpag.nodeFactory().caseThis(); + final Set other = pts.computeIfAbsent(thisRef, k -> new HashSet<>()); + + for (final Stmt s : srcmpag.getInvokeStmts()) { + AbstractInvokeExpr ie = s.getInvokeExpr(); + if (ie instanceof JStaticInvokeExpr) { + for (Iterator it = pta.getCallGraph().edgesOutOf(s); it.hasNext(); ) { + Edge e = it.next(); + SootMethod tgtmtd = e.tgt(); + MethodPAG tgtmpag = pag.getMethodPAG(tgtmtd); + MethodNodeFactory tgtnf = tgtmpag.nodeFactory(); + LocalVarNode tgtThisRef = (LocalVarNode) tgtnf.caseThis(); + // create "THIS" ptr for static method + Set addTo = pts.computeIfAbsent(tgtThisRef, k -> new HashSet<>()); + if (addTo.addAll(other)) { + workList.add(tgtmtd); + } + } + } + } + } + return pts; + } + + public static Object getIR(Object sparkNode) { + if (sparkNode instanceof VarNode) { + return ((VarNode) sparkNode).getVariable(); + } else if (sparkNode instanceof AllocNode) { + return ((AllocNode) sparkNode).getNewExpr(); + } else { // sparkField? + if (sparkNode instanceof Field) { + return ((Field) sparkNode).getField(); + } else { // ArrayElement + return sparkNode; + } + } + } + + public static boolean mustAlias(PTA pta, VarNode v1, VarNode v2) { + PointsToSet v1pts = pta.reachingObjects(v1).toCIPointsToSet(); + PointsToSet v2pts = pta.reachingObjects(v2).toCIPointsToSet(); + return v1pts.pointsToSetEquals(v2pts); + } + + public static void printPts(PTA pta, PointsToSet pts) { + final StringBuffer ret = new StringBuffer(); + for (Iterator it = pts.iterator(); it.hasNext(); ) { + AllocNode n = it.next(); + ret.append("\t").append(n).append("\n"); + } + System.out.print(ret); + } + + public static String getNodeLabel(Node node) { + int num = node.getNumber(); + if (node instanceof LocalVarNode) return "L" + num; + else if (node instanceof ContextVarNode) { + return "L" + num; + } else if (node instanceof GlobalVarNode) return "G" + num; + else if (node instanceof ContextField) return "OF" + num; + else if (node instanceof FieldRefNode) return "VF" + num; + else if (node instanceof AllocNode) return "O" + num; + else throw new RuntimeException("no such node type exists!"); + } + + public static boolean isThrowable(View view, Type type) { + if (type instanceof ClassType) { + return canStoreType(view, type, PTAUtils.getClassType("java.lang.Throwable")); + } + return false; + } + + public static boolean canStoreType(View view, final Type child, final Type parent) { + if (child == parent || child.equals(parent)) { + return true; + } + return view.getTypeHierarchy().isSubtype(parent, child); + } + + public static boolean castNeverFails(View view, Type src, Type dst) { + if (dst == null) return true; + if (dst == src) return true; + if (src == null) return false; + if (dst.equals(src)) return true; + if (src instanceof NullType) return true; + if (dst instanceof NullType) return false; + return canStoreType(view, src, dst); + } + + public static boolean subtypeOfAbstractStringBuilder(Type t) { + if (!(t instanceof ClassType)) { + return false; + } + ClassType rt = (ClassType) t; + String s = rt.toString(); + return (s.equals("java.lang.StringBuffer") || s.equals("java.lang.StringBuilder")); + } + + public static Context plusplusOp(AllocNode heap) { + ContextElement[] array; + int s; + if (heap instanceof ContextAllocNode) { + ContextAllocNode csHeap = (ContextAllocNode) heap; + ContextElements ctxElems = (ContextElements) csHeap.context(); + int ms = ctxElems.size(); + ContextElement[] oldArray = ctxElems.getElements(); + array = new ContextElement[ms + 1]; + array[0] = csHeap.base(); + System.arraycopy(oldArray, 0, array, 1, ms); + s = ms + 1; + } else { + array = new ContextElement[1]; + array[0] = heap; + s = 1; + } + return new ContextElements(array, s); + } + + public static boolean isFakeMainMethod(SootMethod method) { + String sig = ""; + return method.getSignature().toString().equals(sig); + } + + public static boolean isFakeMainClass(ClassType classType) { + String sig = "qilin.pta.FakeMain"; + return classType.toString().equals(sig); + } + + public static boolean isOfPrimitiveBaseType(AllocNode heap) { + if (heap.getType() instanceof ArrayType) { + ArrayType arrayType = (ArrayType) heap.getType(); + return arrayType.getBaseType() instanceof PrimitiveType; + } + return false; + } + + public static boolean isPrimitiveArrayType(Type type) { + if (type instanceof ArrayType) { + ArrayType arrayType = (ArrayType) type; + return arrayType.getElementType() instanceof PrimitiveType; + } + return false; + } + + public static void dumpJimple(PTAScene scene, String outputDir) { + for (SootClass clz : scene.getLibraryClasses()) { + PTAUtils.writeJimple(outputDir, clz); + } + for (SootClass clz : scene.getApplicationClasses()) { + PTAUtils.writeJimple(outputDir, clz); + } + } + + /** Write the jimple file for clz. ParentDir is the absolute path of parent directory. */ + public static void writeJimple(String parentDir, SootClass clz) { + PackageName pkgName = clz.getType().getPackageName(); + String clzName = clz.getType().getClassName(); + File packageDirectory = + new File(parentDir + File.separator + pkgName.getName().replace(".", File.separator)); + + try { + packageDirectory.mkdirs(); + OutputStream streamOut = + new FileOutputStream(packageDirectory + File.separator + clzName + ".jimple"); + PrintWriter writerOut = new PrintWriter(new OutputStreamWriter(streamOut)); + new JimplePrinter().printTo(clz, writerOut); + writerOut.flush(); + writerOut.close(); + streamOut.close(); + + } catch (Exception e) { + logger.error("Error writing jimple to file {}", clz, e); + } + } + + public static String findMainFromMetaInfo(String appPath) { + String mainClass = null; + try { + JarFile jar = new JarFile(appPath); + Enumeration allEntries = jar.entries(); + while (allEntries.hasMoreElements()) { + JarEntry entry = allEntries.nextElement(); + String name = entry.getName(); + if (!name.endsWith(".MF")) { + continue; + } + String urlstring = "jar:file:" + appPath + "!/" + name; + URL url = new URL(urlstring); + Scanner scanner = new Scanner(url.openStream()); + while (scanner.hasNext()) { + String string = scanner.next(); + if ("Main-Class:".equals(string) && scanner.hasNext()) { + mainClass = scanner.next(); + break; + } + } + if (mainClass == null) { + System.out.println("cannot find meta info."); + } + scanner.close(); + jar.close(); + break; + } + } catch (IOException e) { + System.out.println("cannot find meta info."); + } + return mainClass; + } + + private static final Map methodToBody = DataFactory.createMap(); + + public static Body getMethodBody(SootMethod m) { + Body body = methodToBody.get(m); + if (body == null) { + if (m.isConcrete()) { + body = m.getBody(); + } else { + body = Body.builder().setMethodSignature(m.getSignature()).build(); + } + methodToBody.putIfAbsent(m, body); + } + return body; + } + + public static void updateMethodBody(SootMethod m, Body body) { + methodToBody.put(m, body); + } + + public static boolean hasBody(SootMethod m) { + return methodToBody.containsKey(m); + } + + public static boolean isEmptyArray(AllocNode heap) { + Object var = heap.getNewExpr(); + if (var instanceof JNewArrayExpr) { + JNewArrayExpr nae = (JNewArrayExpr) var; + Value sizeVal = nae.getSize(); + if (sizeVal instanceof IntConstant) { + IntConstant size = (IntConstant) sizeVal; + return size.getValue() == 0; + } + } + return false; + } + + public static LocalVarNode paramToArg(PAG pag, Stmt invokeStmt, MethodPAG srcmpag, VarNode pi) { + MethodNodeFactory srcnf = srcmpag.nodeFactory(); + AbstractInvokeExpr ie = invokeStmt.getInvokeExpr(); + Parm mPi = (Parm) pi.getVariable(); + LocalVarNode thisRef = (LocalVarNode) srcnf.caseThis(); + LocalVarNode receiver; + if (ie instanceof AbstractInstanceInvokeExpr) { + AbstractInstanceInvokeExpr iie = (AbstractInstanceInvokeExpr) ie; + Local base = iie.getBase(); + receiver = pag.findLocalVarNode(srcmpag.getMethod(), base, base.getType()); + } else { + // static call + receiver = thisRef; + } + if (mPi.isThis()) { + return receiver; + } else if (mPi.isReturn()) { + if (invokeStmt instanceof JAssignStmt) { + JAssignStmt assignStmt = (JAssignStmt) invokeStmt; + Value mR = assignStmt.getLeftOp(); + return (LocalVarNode) pag.findValNode(mR, srcmpag.getMethod()); + } else { + return null; + } + } else if (mPi.isThrowRet()) { + return srcnf.makeInvokeStmtThrowVarNode(invokeStmt, srcmpag.getMethod()); + } + // Normal formal parameters. + Value arg = ie.getArg(mPi.getIndex()); + if (arg == null) { + return null; + } else { + return pag.findLocalVarNode(srcmpag.getMethod(), arg, arg.getType()); + } + } + + public static View createView() { + /** + * Set the soot class path to point to the default class path appended with the app path (the + * classes dir or the application jar) and jar files in the library dir of the application. + */ + List classPaths = new ArrayList<>(); + List analysisInputLocations = new ArrayList<>(); + PTAConfig.ApplicationConfiguration appConfig = PTAConfig.v().getAppConfig(); + // note that the order is important! + classPaths.add(appConfig.APP_PATH); + analysisInputLocations.add(new JavaClassPathAnalysisInputLocation(appConfig.APP_PATH)); + classPaths.addAll(getLibJars(appConfig.LIB_PATH)); + for (String clazzPath : getLibJars(appConfig.LIB_PATH)) { + analysisInputLocations.add(new JavaClassPathAnalysisInputLocation(clazzPath)); + } + classPaths.addAll(getJreJars(appConfig.JRE)); + for (String clazzPath : getJreJars(appConfig.JRE)) { + analysisInputLocations.add( + new JavaClassPathAnalysisInputLocation(clazzPath, SourceType.Library)); + } + final String classpath = String.join(File.pathSeparator, classPaths); + logger.info("Soot ClassPath: {}", classpath); + return new JavaView(analysisInputLocations); + } + + /** Returns a collection of files, one for each of the jar files in the app's lib folder */ + private static Collection getLibJars(String LIB_PATH) { + if (LIB_PATH == null) { + return Collections.emptySet(); + } + File libFile = new File(LIB_PATH); + if (libFile.exists()) { + if (libFile.isDirectory()) { + return FileUtils.listFiles(libFile, new String[] {"jar"}, true).stream() + .map(File::toString) + .collect(Collectors.toList()); + } else if (libFile.isFile()) { + if (libFile.getName().endsWith(".jar")) { + return Collections.singletonList(LIB_PATH); + } + logger.error( + "Project not configured properly. Application library path {} is not a jar file.", + libFile); + System.exit(1); + } + } + logger.error( + "Project not configured properly. Application library path {} is not correct.", libFile); + System.exit(1); + return null; + } + + private static Collection getJreJars(String JRE) { + if (JRE == null) { + return Collections.emptySet(); + } + final String jreLibDir = JRE + File.separator + "lib"; + return FileUtils.listFiles(new File(jreLibDir), new String[] {"jar"}, false).stream() + .map(File::toString) + .collect(Collectors.toList()); + } +} diff --git a/sootup.qilin/src/main/java/qilin/util/Pair.java b/sootup.qilin/src/main/java/qilin/util/Pair.java new file mode 100644 index 00000000000..1e4557ee87c --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/Pair.java @@ -0,0 +1,59 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.util; + +import java.util.Objects; + +public class Pair { + + private final T1 first; + private final T2 second; + + public Pair(T1 first, T2 second) { + this.first = first; + this.second = second; + } + + public T1 getFirst() { + return first; + } + + public T2 getSecond() { + return second; + } + + @Override + public int hashCode() { + return Objects.hash(first, second); + } + + @Override + public boolean equals(Object o) { + if (o instanceof Pair) { + Pair anoPair = (Pair) o; + return Objects.equals(first, anoPair.first) && Objects.equals(second, anoPair.second); + } + return false; + } + + @Override + public String toString() { + return "<" + first + ", " + second + ">"; + } +} diff --git a/sootup.qilin/src/main/java/qilin/util/Stopwatch.java b/sootup.qilin/src/main/java/qilin/util/Stopwatch.java new file mode 100644 index 00000000000..a927afc061d --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/Stopwatch.java @@ -0,0 +1,71 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.util; + +public class Stopwatch { + private final String name; + private long elapsedTime; + private long startTime; + private boolean inCounting; + + public static Stopwatch newAndStart(final String name) { + Stopwatch stopwatch = new Stopwatch(name); + stopwatch.start(); + return stopwatch; + } + + private Stopwatch(final String name) { + this.elapsedTime = 0L; + this.inCounting = false; + this.name = name; + } + + private void start() { + if (!this.inCounting) { + this.inCounting = true; + this.startTime = System.currentTimeMillis(); + } + } + + public void stop() { + if (this.inCounting) { + this.elapsedTime += System.currentTimeMillis() - this.startTime; + this.inCounting = false; + } + } + + public float elapsed() { + return this.elapsedTime / 1000.0f; + } + + public void reset() { + this.elapsedTime = 0L; + this.inCounting = false; + } + + public void restart() { + reset(); + start(); + } + + @Override + public String toString() { + return String.format("%s elapsed time: %.2fs", this.name, this.elapsed()); + } +} diff --git a/sootup.qilin/src/main/java/qilin/util/StringNumberer.java b/sootup.qilin/src/main/java/qilin/util/StringNumberer.java new file mode 100644 index 00000000000..71e9dd5cede --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/StringNumberer.java @@ -0,0 +1,53 @@ +package qilin.util; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 2002 Ondrej Lhotak + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import heros.ThreadSafe; +import java.util.HashMap; +import java.util.Map; + +/** + * A class that numbers strings, so they can be placed in bitsets. + * + * @author Ondrej Lhotak + */ +@ThreadSafe +public class StringNumberer extends ArrayNumberer { + + private final Map stringToNumbered = + new HashMap(1024); + + public synchronized NumberedString findOrAdd(String s) { + NumberedString ret = stringToNumbered.get(s); + if (ret == null) { + ret = new NumberedString(s); + stringToNumbered.put(s, ret); + add(ret); + } + return ret; + } + + public NumberedString find(String s) { + return stringToNumbered.get(s); + } +} diff --git a/sootup.qilin/src/main/java/qilin/util/Triple.java b/sootup.qilin/src/main/java/qilin/util/Triple.java new file mode 100644 index 00000000000..9c37e3db3cd --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/Triple.java @@ -0,0 +1,66 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.util; + +import java.util.Objects; + +public class Triple { + private final T1 first; + private final T2 second; + private final T3 third; + + public Triple(final T1 first, final T2 second, final T3 third) { + this.first = first; + this.second = second; + this.third = third; + } + + public T1 getFirst() { + return this.first; + } + + public T2 getSecond() { + return this.second; + } + + public T3 getThird() { + return this.third; + } + + @Override + public int hashCode() { + return Objects.hash(this.first, this.second, this.third); + } + + @Override + public boolean equals(final Object o) { + if (o instanceof Triple) { + final Triple anoTriple = (Triple) o; + return Objects.equals(this.first, anoTriple.first) + && Objects.equals(this.second, anoTriple.second) + && Objects.equals(this.third, anoTriple.third); + } + return false; + } + + @Override + public String toString() { + return "<" + this.first + ", " + this.second + ", " + this.third + ">"; + } +} diff --git a/sootup.qilin/src/main/java/qilin/util/UnionFindSet.java b/sootup.qilin/src/main/java/qilin/util/UnionFindSet.java new file mode 100644 index 00000000000..8e70b93b1a0 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/UnionFindSet.java @@ -0,0 +1,94 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.util; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +public class UnionFindSet { + private final Map entries; + private int nrsets; + + public UnionFindSet(final Collection elems) { + this.entries = new HashMap<>(); + elems.forEach(elem -> this.entries.put(elem, new Entry(elem))); + this.nrsets = this.entries.size(); + } + + public boolean union(final E e1, final E e2) { + final Entry root1 = this.findRoot(this.entries.get(e1)); + final Entry root2 = this.findRoot(this.entries.get(e2)); + if (root1 == root2) { + return false; + } + if (root1.rank < root2.rank) { + root1.parent = root2; + } else if (root1.rank > root2.rank) { + root2.parent = root1; + } else { + root2.parent = root1; + ++root2.rank; + } + --this.nrsets; + return true; + } + + public boolean isConnected(final E e1, final E e2) { + final Entry root1 = this.findRoot(this.entries.get(e1)); + final Entry root2 = this.findRoot(this.entries.get(e2)); + return root1 == root2; + } + + public E find(final E e) { + final Entry ent = this.findRoot(this.entries.get(e)); + return ent.elem; + } + + public int numberOfSets() { + return this.nrsets; + } + + public Collection> getDisjointSets() { + return this.entries.keySet().stream() + .collect(Collectors.groupingBy(this::find, Collectors.toSet())) + .values(); + } + + private Entry findRoot(final Entry ent) { + if (ent.parent != ent) { + ent.parent = this.findRoot(ent.parent); + } + return ent.parent; + } + + private class Entry { + private final E elem; + private Entry parent; + private int rank; + + private Entry(final E elem) { + this.elem = elem; + this.parent = this; + this.rank = 0; + } + } +} diff --git a/sootup.qilin/src/main/java/qilin/util/Util.java b/sootup.qilin/src/main/java/qilin/util/Util.java new file mode 100644 index 00000000000..7f2a17a949b --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/Util.java @@ -0,0 +1,87 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.util; + +import java.io.*; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Util { + private static final Logger logger = LoggerFactory.getLogger(Util.class); + + public static byte[] toUtf8(String s) { + try { + ByteArrayOutputStream bs = new ByteArrayOutputStream(s.length()); + DataOutputStream d = new DataOutputStream(bs); + d.writeUTF(s); + return bs.toByteArray(); + } catch (IOException e) { + logger.debug("Some sort of IO exception in toUtf8 with " + s); + } + return null; + } + + public static boolean addToMap(Map> map, K key, V value) { + return map.computeIfAbsent(key, k -> DataFactory.createSet()).add(value); + } + + public static boolean removeFromMap(Map> map, K key, V value) { + if (!map.containsKey(key)) { + return false; + } + return map.get(key).remove(value); + } + + public static void add(Map map, T name, String... aliases) { + for (String alias : aliases) { + map.put(alias, name); + } + } + + public static String[] concat(String[] a, String[] b) { + String[] c = new String[a.length + b.length]; + System.arraycopy(a, 0, c, 0, a.length); + System.arraycopy(b, 0, c, a.length, b.length); + return c; + } + + public static void writeToFile(String file, String content) { + try { + File mfile = new File(file); + if (!mfile.exists()) { + System.out.println(file); + mfile.createNewFile(); + } + FileWriter writer = new FileWriter(mfile); + writer.write(content); + writer.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private static final Pattern qPat = Pattern.compile("'"); + + public static String stripQuotes(CharSequence s) { + return qPat.matcher(s).replaceAll(""); + } +} diff --git a/sootup.qilin/src/main/java/qilin/util/collect/SetFactory.java b/sootup.qilin/src/main/java/qilin/util/collect/SetFactory.java new file mode 100644 index 00000000000..b500eee193e --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/collect/SetFactory.java @@ -0,0 +1,38 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.util.collect; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class SetFactory { + private final Map, Set> sets; + + public SetFactory() { + this.sets = new HashMap<>(); + } + + public Set get(final Set set) { + if (!this.sets.containsKey(set)) { + this.sets.put(set, set); + } + return this.sets.get(set); + } +} diff --git a/sootup.qilin/src/main/java/qilin/util/graph/ConcurrentDirectedGraphImpl.java b/sootup.qilin/src/main/java/qilin/util/graph/ConcurrentDirectedGraphImpl.java new file mode 100644 index 00000000000..fcc2fe33216 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/graph/ConcurrentDirectedGraphImpl.java @@ -0,0 +1,63 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.util.graph; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +public class ConcurrentDirectedGraphImpl implements DirectedGraph { + protected Set nodes; + protected Map> preds; + protected Map> succs; + + public ConcurrentDirectedGraphImpl() { + this.nodes = ConcurrentHashMap.newKeySet(); + this.preds = new ConcurrentHashMap<>(); + this.succs = new ConcurrentHashMap<>(); + } + + public void addNode(final N node) { + this.nodes.add(node); + } + + public void addEdge(final N from, final N to) { + this.addNode(from); + this.addNode(to); + this.preds.computeIfAbsent(to, k -> ConcurrentHashMap.newKeySet()).add(from); + this.succs.computeIfAbsent(from, k -> ConcurrentHashMap.newKeySet()).add(to); + } + + @Override + public Collection allNodes() { + return this.nodes; + } + + @Override + public Collection predsOf(final N n) { + return this.preds.getOrDefault(n, Collections.emptySet()); + } + + @Override + public Collection succsOf(final N n) { + return this.succs.getOrDefault(n, Collections.emptySet()); + } +} diff --git a/sootup.qilin/src/main/java/qilin/util/graph/DirectedGraph.java b/sootup.qilin/src/main/java/qilin/util/graph/DirectedGraph.java new file mode 100644 index 00000000000..1af26413808 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/graph/DirectedGraph.java @@ -0,0 +1,59 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.util.graph; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.Stack; +import java.util.stream.Collectors; + +public interface DirectedGraph { + Collection allNodes(); + + Collection predsOf(final N p); + + Collection succsOf(final N p); + + /* no cache, very slow.*/ + default Collection computeReachableNodes(N source) { + Set reachableNodes = new HashSet<>(); + Stack stack = new Stack<>(); + stack.push(source); + while (!stack.isEmpty()) { + N node = stack.pop(); + if (reachableNodes.add(node)) { + stack.addAll(succsOf(node)); + } + } + return reachableNodes; + } + + default Collection computeRootNodes() { + return allNodes().stream() + .filter(node -> predsOf(node).size() == 0) + .collect(Collectors.toSet()); + } + + default Collection computeTailNodes() { + return allNodes().stream() + .filter(node -> succsOf(node).size() == 0) + .collect(Collectors.toSet()); + } +} diff --git a/sootup.qilin/src/main/java/qilin/util/graph/DirectedGraphImpl.java b/sootup.qilin/src/main/java/qilin/util/graph/DirectedGraphImpl.java new file mode 100644 index 00000000000..148f27b1437 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/graph/DirectedGraphImpl.java @@ -0,0 +1,59 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.util.graph; + +import java.util.*; + +public class DirectedGraphImpl implements DirectedGraph { + protected Set nodes; + protected Map> preds; + protected Map> succs; + + public DirectedGraphImpl() { + this.nodes = new HashSet<>(); + this.preds = new HashMap<>(); + this.succs = new HashMap<>(); + } + + public void addNode(final N node) { + this.nodes.add(node); + } + + public void addEdge(final N from, final N to) { + this.addNode(from); + this.addNode(to); + this.preds.computeIfAbsent(to, k -> new HashSet<>()).add(from); + this.succs.computeIfAbsent(from, k -> new HashSet<>()).add(to); + } + + @Override + public Collection allNodes() { + return this.nodes; + } + + @Override + public Collection predsOf(final N n) { + return this.preds.getOrDefault(n, Collections.emptySet()); + } + + @Override + public Collection succsOf(final N n) { + return this.succs.getOrDefault(n, Collections.emptySet()); + } +} diff --git a/sootup.qilin/src/main/java/qilin/util/graph/MergedNode.java b/sootup.qilin/src/main/java/qilin/util/graph/MergedNode.java new file mode 100644 index 00000000000..25f76580c1e --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/graph/MergedNode.java @@ -0,0 +1,65 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.util.graph; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +public class MergedNode { + private Set> preds; + private Set> succs; + private final Set content; + + public MergedNode(final Collection content) { + this.content = new HashSet<>(content); + } + + public void addPred(final MergedNode pred) { + if (this.preds == null) { + this.preds = new HashSet<>(4); + } + this.preds.add(pred); + } + + public Set> getPreds() { + return (this.preds == null) ? Collections.emptySet() : this.preds; + } + + public void addSucc(final MergedNode succ) { + if (this.succs == null) { + this.succs = new HashSet<>(4); + } + this.succs.add(succ); + } + + public Set> getSuccs() { + return (this.succs == null) ? Collections.emptySet() : this.succs; + } + + public Set getContent() { + return this.content; + } + + @Override + public String toString() { + return this.content.toString(); + } +} diff --git a/sootup.qilin/src/main/java/qilin/util/graph/Reachability.java b/sootup.qilin/src/main/java/qilin/util/graph/Reachability.java new file mode 100644 index 00000000000..1afb0b80c9b --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/graph/Reachability.java @@ -0,0 +1,71 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.util.graph; + +import java.util.*; + +public class Reachability { + private final DirectedGraph graph; + private final Map> reachableNodes; + private final Map> reachToNodes; + + public Reachability(final DirectedGraph graph) { + this.reachableNodes = new HashMap<>(); + this.reachToNodes = new HashMap<>(); + this.graph = graph; + } + + public Set reachableNodesFrom(final N source) { + if (!this.reachableNodes.containsKey(source)) { + final Set visited = new HashSet<>(); + final Deque stack = new ArrayDeque<>(); + stack.push(source); + while (!stack.isEmpty()) { + final N node = stack.pop(); + visited.add(node); + this.graph.succsOf(node).stream().filter(n -> !visited.contains(n)).forEach(stack::push); + } + this.reachableNodes.put(source, visited); + } + return this.reachableNodes.get(source); + } + + public Set nodesReach(final N target) { + if (!this.reachToNodes.containsKey(target)) { + final Set visited = new HashSet<>(); + final Deque stack = new ArrayDeque<>(); + stack.push(target); + while (!stack.isEmpty()) { + final N node = stack.pop(); + visited.add(node); + this.graph.predsOf(node).stream().filter(n -> !visited.contains(n)).forEach(stack::push); + } + this.reachToNodes.put(target, visited); + } + return this.reachToNodes.get(target); + } + + public Set passedNodes(final N source, final N target) { + final Set reachableFromSource = this.reachableNodesFrom(source); + final Set reachToTarget = this.nodesReach(target); + final Set ret = new HashSet<>(reachableFromSource); + ret.retainAll(reachToTarget); + return ret; + } +} diff --git a/sootup.qilin/src/main/java/qilin/util/graph/SCCMergedGraph.java b/sootup.qilin/src/main/java/qilin/util/graph/SCCMergedGraph.java new file mode 100644 index 00000000000..efb023b4497 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/graph/SCCMergedGraph.java @@ -0,0 +1,73 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.util.graph; + +import java.util.*; + +public class SCCMergedGraph implements DirectedGraph> { + private Set> nodes; + private final Map> nodeMap = new HashMap<>(); + + public SCCMergedGraph(final DirectedGraph graph) { + this.init(graph); + } + + public MergedNode getMergedNode(N contentNode) { + return nodeMap.get(contentNode); + } + + @Override + public Collection> allNodes() { + return this.nodes; + } + + @Override + public Collection> predsOf(final MergedNode node) { + return node.getPreds(); + } + + @Override + public Collection> succsOf(final MergedNode node) { + return node.getSuccs(); + } + + private void init(final DirectedGraph graph) { + this.nodes = new HashSet<>(); + final StronglyConnectedComponents scc = new StronglyConnectedComponents<>(graph); + scc.getComponents() + .forEach( + component -> { + final MergedNode node2 = new MergedNode<>(component); + component.forEach(n -> nodeMap.put(n, node2)); + this.nodes.add(node2); + }); + this.nodes.forEach( + node -> + node.getContent().stream() + .map(graph::succsOf) + .flatMap(Collection::stream) + .map(nodeMap::get) + .filter(succ -> succ != node) + .forEach( + succ -> { + node.addSucc(succ); + succ.addPred(node); + })); + } +} diff --git a/sootup.qilin/src/main/java/qilin/util/graph/StronglyConnectedComponents.java b/sootup.qilin/src/main/java/qilin/util/graph/StronglyConnectedComponents.java new file mode 100644 index 00000000000..f985550fe91 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/graph/StronglyConnectedComponents.java @@ -0,0 +1,100 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.util.graph; + +import java.util.*; + +public class StronglyConnectedComponents { + private final List> componentList; + private final List> trueComponentList; + private int index; + private Map indexForNode; + private Map lowlinkForNode; + private Stack stack; + private DirectedGraph graph; + + public StronglyConnectedComponents(final DirectedGraph graph) { + this.componentList = new ArrayList<>(); + this.trueComponentList = new ArrayList<>(); + this.index = 0; + this.graph = graph; + this.stack = new Stack<>(); + this.indexForNode = new HashMap<>(); + this.lowlinkForNode = new HashMap<>(); + for (final N node : graph.allNodes()) { + if (!this.indexForNode.containsKey(node)) { + this.recurse(node); + } + } + this.validate(graph, this.componentList); + this.indexForNode = null; + this.lowlinkForNode = null; + this.stack = null; + this.graph = null; + } + + public List> getComponents() { + return this.componentList; + } + + public List> getTrueComponents() { + return this.trueComponentList; + } + + private void recurse(final N node) { + this.indexForNode.put(node, this.index); + this.lowlinkForNode.put(node, this.index); + ++this.index; + this.stack.push(node); + for (final N succ : this.graph.succsOf(node)) { + if (!this.indexForNode.containsKey(succ)) { + this.recurse(succ); + this.lowlinkForNode.put( + node, Math.min(this.lowlinkForNode.get(node), this.lowlinkForNode.get(succ))); + } else { + if (!this.stack.contains(succ)) { + continue; + } + this.lowlinkForNode.put( + node, Math.min(this.lowlinkForNode.get(node), this.indexForNode.get(succ))); + } + } + if (this.lowlinkForNode.get(node) == (int) this.indexForNode.get(node)) { + final List scc = new ArrayList<>(); + N v2; + do { + v2 = this.stack.pop(); + scc.add(v2); + } while (node != v2); + this.componentList.add(scc); + if (scc.size() > 1) { + this.trueComponentList.add(scc); + } else { + final N n = scc.get(0); + if (this.graph.succsOf(n).contains(n)) { + this.trueComponentList.add(scc); + } + } + } + } + + private void validate(final DirectedGraph graph, final List> SCCs) { + assert graph.allNodes().size() == SCCs.stream().mapToInt(List::size).sum(); + } +} diff --git a/sootup.qilin/src/main/java/qilin/util/graph/TopologicalSorter.java b/sootup.qilin/src/main/java/qilin/util/graph/TopologicalSorter.java new file mode 100644 index 00000000000..07294cd3c8a --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/graph/TopologicalSorter.java @@ -0,0 +1,62 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.util.graph; + +import java.util.*; + +public class TopologicalSorter { + private DirectedGraph graph; + private List sortedList; + private Set visited; + + public List sort(final DirectedGraph graph) { + return this.sort(graph, false); + } + + public List sort(final DirectedGraph graph, final boolean reverse) { + this.initialize(graph); + graph.allNodes().stream().filter(n -> graph.succsOf(n).isEmpty()).forEach(this::visit); + List result = this.sortedList; + if (reverse) { + Collections.reverse(result); + } + this.clear(); + return result; + } + + private void initialize(final DirectedGraph graph) { + this.graph = graph; + this.sortedList = new LinkedList<>(); + this.visited = new HashSet<>(); + } + + private void visit(final N node) { + if (!this.visited.contains(node)) { + this.visited.add(node); + this.graph.predsOf(node).forEach(this::visit); + this.sortedList.add(node); + } + } + + private void clear() { + this.graph = null; + this.sortedList = null; + this.visited = null; + } +} diff --git a/sootup.qilin/src/main/java/qilin/util/graph/Tree.java b/sootup.qilin/src/main/java/qilin/util/graph/Tree.java new file mode 100644 index 00000000000..4d75d4a77f7 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/graph/Tree.java @@ -0,0 +1,37 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.util.graph; + +import java.util.Collection; + +public interface Tree { + N getRoot(); + + Collection getLeaves(); + + boolean isALeaf(N n); + + Collection getAllNodes(); + + int size(); + + N parentOf(N n); + + Collection childrenOf(N n); +} diff --git a/sootup.qilin/src/main/java/qilin/util/graph/TreeImpl.java b/sootup.qilin/src/main/java/qilin/util/graph/TreeImpl.java new file mode 100644 index 00000000000..8c02d064a45 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/graph/TreeImpl.java @@ -0,0 +1,93 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.util.graph; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +public class TreeImpl implements Tree { + Map> data2Node; + TreeNode root; + + public TreeImpl() { + this.data2Node = new HashMap<>(); + } + + @Override + public N getRoot() { + if (root == null) { + for (TreeNode n : data2Node.values()) { + if (n.isRoot()) { + root = n; + break; + } + } + } + return root != null ? root.getElem() : null; + } + + @Override + public Collection getLeaves() { + return getAllNodes().stream().filter(this::isALeaf).collect(Collectors.toSet()); + } + + @Override + public boolean isALeaf(N o) { + return data2Node.containsKey(o) && data2Node.get(o).isLeaf(); + } + + @Override + public Collection getAllNodes() { + return data2Node.keySet(); + } + + public Collection> getAllTreeNodes() { + return data2Node.values(); + } + + @Override + public int size() { + return data2Node.size(); + } + + @Override + public N parentOf(N o) { + return data2Node.containsKey(o) ? data2Node.get(o).getParent().getElem() : null; + } + + @Override + public Collection childrenOf(N o) { + if (data2Node.containsKey(o)) { + TreeNode node = data2Node.get(o); + return node.getChildren().stream().map(TreeNode::getElem).collect(Collectors.toSet()); + } + return Collections.emptySet(); + } + + public boolean addTreeEdge(N parent, N child) { + TreeNode p = data2Node.computeIfAbsent(parent, k -> new TreeNode<>(parent)); + TreeNode c = data2Node.computeIfAbsent(child, k -> new TreeNode<>(child)); + boolean f = p.addChild(c); + c.setParent(p); + return f; + } +} diff --git a/sootup.qilin/src/main/java/qilin/util/graph/TreeNode.java b/sootup.qilin/src/main/java/qilin/util/graph/TreeNode.java new file mode 100644 index 00000000000..b9d2142ede8 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/graph/TreeNode.java @@ -0,0 +1,61 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.util.graph; + +import java.util.HashSet; +import java.util.Set; + +public class TreeNode { + private final D elem; + private TreeNode parent; + private final Set> children; + + public TreeNode(D e) { + this.elem = e; + this.children = new HashSet<>(); + } + + public D getElem() { + return elem; + } + + public TreeNode getParent() { + return parent; + } + + public boolean isLeaf() { + return this.children.isEmpty(); + } + + public boolean isRoot() { + return this.parent == null; + } + + public void setParent(TreeNode parent) { + this.parent = parent; + } + + public Set> getChildren() { + return children; + } + + public boolean addChild(TreeNode child) { + return this.children.add(child); + } +} diff --git a/sootup.qilin/src/main/java/qilin/util/package.html b/sootup.qilin/src/main/java/qilin/util/package.html new file mode 100644 index 00000000000..38bdbd94acd --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/package.html @@ -0,0 +1,27 @@ + + +Soot utility classes + +Generally useful utility classes for Soot. + + diff --git a/sootup.qilin/src/main/java/qilin/util/queue/ChunkedQueue.java b/sootup.qilin/src/main/java/qilin/util/queue/ChunkedQueue.java new file mode 100644 index 00000000000..ac21c0e0286 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/queue/ChunkedQueue.java @@ -0,0 +1,97 @@ +package qilin.util.queue; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 2003 Ondrej Lhotak + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +/** + * A queue of Object's. One can add objects to the queue, and they are later read by a QueueReader. + * One can create arbitrary numbers of QueueReader's for a queue, and each one receives all the + * Object's that are added. Only objects that have not been read by all the QueueReader's are kept. + * A QueueReader only receives the Object's added to the queue after the QueueReader was + * created. + * + * @author Ondrej Lhotak + */ +@SuppressWarnings("unchecked") +public class ChunkedQueue { + + protected static final Object NULL_CONST = new Object(); + protected static final Object DELETED_CONST = new Object(); + + protected static final int LENGTH = 60; + protected Object[] q; + protected int index; + + public ChunkedQueue() { + q = new Object[LENGTH]; + index = 0; + } + + /** Add an object to the queue. */ + public void add(E o) { + if (o == null) { + o = (E) NULL_CONST; + } + if (index == LENGTH - 1) { + Object[] temp = new Object[LENGTH]; + q[index] = temp; + q = temp; + index = 0; + } + q[index++] = o; + } + + /** Create reader which will read objects from the queue. */ + public QueueReader reader() { + return new QueueReader((E[]) q, index); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("["); + boolean isFirst = true; + + int idx = index; + Object[] curArray = q; + while (idx < curArray.length) { + Object curObj = curArray[idx]; + if (curObj == null) { + break; + } + if (isFirst) { + isFirst = false; + } else { + sb.append(", "); + } + if (curObj instanceof Object[]) { + curArray = (Object[]) curObj; + idx = 0; + } else { + sb.append(curObj.toString()); + idx++; + } + } + sb.append("]"); + return sb.toString(); + } +} diff --git a/sootup.qilin/src/main/java/qilin/util/queue/QueueReader.java b/sootup.qilin/src/main/java/qilin/util/queue/QueueReader.java new file mode 100644 index 00000000000..b5796c70778 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/queue/QueueReader.java @@ -0,0 +1,196 @@ +package qilin.util.queue; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 2003 Ondrej Lhotak + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import java.util.Collection; +import java.util.Collections; +import java.util.NoSuchElementException; +import qilin.util.Invalidable; + +/** + * A queue of Object's. One can add objects to the queue, and they are later read by a QueueReader. + * One can create arbitrary numbers of QueueReader's for a queue, and each one receives all the + * Object's that are added. Only objects that have not been read by all the QueueReader's are kept. + * A QueueReader only receives the Object's added to the queue after the QueueReader was + * created. + * + * @author Ondrej Lhotak + */ +public class QueueReader implements java.util.Iterator { + protected E[] q; + protected int index; + + protected QueueReader(E[] q, int index) { + this.q = q; + this.index = index; + } + + /** Returns (and removes) the next object in the queue, or null if there are none. */ + @SuppressWarnings("unchecked") + public E next() { + Object ret = null; + do { + if (q[index] == null) { + throw new NoSuchElementException(); + } + if (index == q.length - 1) { + q = (E[]) q[index]; + index = 0; + if (q[index] == null) { + throw new NoSuchElementException(); + } + } + ret = q[index]; + if (ret == ChunkedQueue.NULL_CONST) { + ret = null; + } + index++; + } while (skip(ret)); + return (E) ret; + } + + protected boolean skip(Object ret) { + if (ret instanceof Invalidable) { + final Invalidable invalidable = (Invalidable) ret; + if (invalidable.isInvalid()) { + return true; + } + } + return ret == ChunkedQueue.DELETED_CONST; + } + + /** Returns true iff there is currently another object in the queue. */ + @SuppressWarnings("unchecked") + public boolean hasNext() { + do { + E ret = q[index]; + if (ret == null) { + return false; + } + if (index == q.length - 1) { + q = (E[]) ret; + index = 0; + if (q[index] == null) { + return false; + } + } + if (skip(ret)) { + index++; + } else { + return true; + } + } while (true); + } + + /** + * Removes an element from the underlying queue. This operation can only delete elements that have + * not yet been consumed by this reader. + * + * @param o The element to remove + */ + public void remove(E o) { + if (o instanceof Invalidable) { + ((Invalidable) o).invalidate(); + return; + } + remove(Collections.singleton(o)); + } + + /** + * Removes elements from the underlying queue. This operation can only delete elements that have + * not yet been consumed by this reader. + * + * @param toRemove The elements to remove + */ + @SuppressWarnings("unchecked") + public void remove(Collection toRemove) { + boolean allInvalidable = true; + for (E o : toRemove) { + if (!(o instanceof Invalidable)) { + allInvalidable = false; + continue; + } + + ((Invalidable) o).invalidate(); + } + if (allInvalidable) { + return; + } + int idx = 0; + Object[] curQ = q; + while (curQ[idx] != null) { + // Do we need to switch to a new list? + if (idx == curQ.length - 1) { + curQ = (E[]) curQ[idx]; + idx = 0; + } + + // Is this the element to delete? + if (toRemove.contains(curQ[idx])) { + curQ[idx] = ChunkedQueue.DELETED_CONST; + } + + // Next element + idx++; + } + } + + @SuppressWarnings("unchecked") + public void remove() { + q[index - 1] = (E) ChunkedQueue.DELETED_CONST; + } + + public QueueReader clone() { + return new QueueReader(q, index); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("["); + boolean isFirst = true; + + int idx = index; + Object[] curArray = q; + while (idx < curArray.length) { + Object curObj = curArray[idx]; + if (curObj == null) { + break; + } + if (isFirst) { + isFirst = false; + } else { + sb.append(", "); + } + if (curObj instanceof Object[]) { + curArray = (Object[]) curObj; + idx = 0; + } else { + sb.append(curObj.toString()); + idx++; + } + } + sb.append("]"); + return sb.toString(); + } +} diff --git a/sootup.qilin/src/main/java/qilin/util/queue/UniqueQueue.java b/sootup.qilin/src/main/java/qilin/util/queue/UniqueQueue.java new file mode 100644 index 00000000000..975ab7692e4 --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/queue/UniqueQueue.java @@ -0,0 +1,136 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.util.queue; + +import java.util.*; + +public class UniqueQueue implements Queue { + private final Set set; + private final Queue queue; + + public UniqueQueue() { + this.set = new HashSet<>(); + this.queue = new LinkedList<>(); + } + + @Override + public boolean add(T t) { + if (set.contains(t)) { + return false; + } else { + set.add(t); + queue.add(t); + return true; + } + } + + @Override + public void clear() { + set.clear(); + queue.clear(); + } + + @Override + public boolean offer(T t) { + return add(t); + } + + @Override + public T remove() { + return poll(); + } + + @Override + public T poll() { + T t = queue.poll(); + if (t != null) { + set.remove(t); + } + return t; + } + + @Override + public T peek() { + return queue.peek(); + } + + @Override + public int size() { + return set.size(); + } + + @Override + public boolean isEmpty() { + return set.isEmpty(); + } + + @Override + public boolean contains(Object o) { + throw new UnsupportedOperationException(); + } + + @Override + public Iterator iterator() { + throw new UnsupportedOperationException(); + } + + @Override + public Object[] toArray() { + throw new UnsupportedOperationException(); + } + + @Override + public T1[] toArray(T1[] t1s) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(Object o) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean containsAll(Collection collection) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(Collection collection) { + boolean ret = false; + for (T t : collection) { + ret |= add(t); + } + return ret; + } + + @Override + public boolean removeAll(Collection collection) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean retainAll(Collection collection) { + throw new UnsupportedOperationException(); + } + + @Override + public T element() { + throw new UnsupportedOperationException(); + } +} diff --git a/sootup.qilin/src/main/java/qilin/util/queue/package.html b/sootup.qilin/src/main/java/qilin/util/queue/package.html new file mode 100644 index 00000000000..2eb0b82ce6b --- /dev/null +++ b/sootup.qilin/src/main/java/qilin/util/queue/package.html @@ -0,0 +1,27 @@ + + +Soot queue classes + +An efficient queue implementation to be used generally within Soot. + + diff --git a/sootup.qilin/src/test/java/qilin/microben/context/cfa/CFA1k0.java b/sootup.qilin/src/test/java/qilin/microben/context/cfa/CFA1k0.java new file mode 100644 index 00000000000..eea4e560f18 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/cfa/CFA1k0.java @@ -0,0 +1,36 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.context.cfa; + +import qilin.microben.utils.Assert; + +class CFA1k0 { + static Object id(Object a) { + return a; + } + + public static void main(String[] argv) { + Object o1 = new Object(); + Object o2 = new Object(); + Object v1 = id(o1); + Object v2 = id(o2); + Assert.notAlias(v1, v2); + Assert.mayAlias(v1, o1); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/context/cfa/CFA1k1.java b/sootup.qilin/src/test/java/qilin/microben/context/cfa/CFA1k1.java new file mode 100644 index 00000000000..653ab5d391f --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/cfa/CFA1k1.java @@ -0,0 +1,45 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.context.cfa; + +import qilin.microben.utils.Assert; + +public class CFA1k1 { + public static void main(String[] args) { + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + bar(o1); + bar(o2); + } + + static void bar(Object o) { + Object o5 = new Object(); + Object v5 = id(o5); + Assert.notAlias(v5, o); + id2(o); + } + + static Object id(Object o) { + return o; + } + + static Object id2(Object o) { + return id(o); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/context/cfa/CFA1k2.java b/sootup.qilin/src/test/java/qilin/microben/context/cfa/CFA1k2.java new file mode 100644 index 00000000000..85a1e129de1 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/cfa/CFA1k2.java @@ -0,0 +1,50 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.context.cfa; + +import qilin.microben.utils.Assert; + +class CFA1k2 { + static class A { + Object id(Object a) { + return a; + } + + Object id2(Object a) { + return id(a); + } + } + + static class A2 extends A { + Object id(Object a) { + return a; + } + } + + public static void main(String[] argv) { + A a1 = new A(); // A1 + A a2 = new A2(); // A2 + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + Object v1 = a1.id2(o1); + Object v2 = a2.id2(o2); + Assert.notAlias(v1, v2); + Assert.mayAlias(v1, o1); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/context/cfa/CFA2k.java b/sootup.qilin/src/test/java/qilin/microben/context/cfa/CFA2k.java new file mode 100644 index 00000000000..bc4c0f0504c --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/cfa/CFA2k.java @@ -0,0 +1,40 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.context.cfa; + +import qilin.microben.utils.Assert; + +class CFA2k { + static Object id(Object a) { + return a; + } + + static Object id2(Object a) { + return id(a); + } + + public static void main(String[] argv) { + Object o1 = new Object(); + Object o2 = new Object(); + Object v1 = id2(o1); + Object v2 = id2(o2); + Assert.notAlias(v1, v2); + Assert.mayAlias(v1, o1); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/context/cfa/log.txt b/sootup.qilin/src/test/java/qilin/microben/context/cfa/log.txt new file mode 100644 index 00000000000..ba720af9aff --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/cfa/log.txt @@ -0,0 +1,5 @@ +insens-qilin.core: 1 passed, 6 failed. +e-2o: 1 passed, 6 failed. +2c: 7 passed +j-ci: 1 passed, 6 failed. +m-ci: 1 passed, 6 failed. \ No newline at end of file diff --git a/sootup.qilin/src/test/java/qilin/microben/context/collections/ArrayList0.java b/sootup.qilin/src/test/java/qilin/microben/context/collections/ArrayList0.java new file mode 100644 index 00000000000..494574bd092 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/collections/ArrayList0.java @@ -0,0 +1,41 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.context.collections; + +import java.util.ArrayList; +import qilin.microben.utils.Assert; + +// need at least 2obj. +public class ArrayList0 { + + public static void main(String[] args) { + ArrayList list1 = new ArrayList<>(); + ArrayList list2 = new ArrayList<>(); + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + Object o3 = new Object(); // O3 + list1.add(o1); + list1.add(o2); + list2.add(o3); + Object v1 = list1.get(1); + Object v2 = list2.get(0); + Assert.mayAlias(v1, o2); + Assert.notAlias(v1, v2); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/context/collections/HashMap0.java b/sootup.qilin/src/test/java/qilin/microben/context/collections/HashMap0.java new file mode 100644 index 00000000000..e21adf8f08b --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/collections/HashMap0.java @@ -0,0 +1,39 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.context.collections; + +import java.util.HashMap; +import qilin.microben.utils.Assert; + +public class HashMap0 { + public static void main(String[] args) { + HashMap map1 = new HashMap<>(); + HashMap map2 = new HashMap<>(); + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + Object o3 = new Object(); // O3 + map1.put("first", o1); + map1.put("second", o2); + map2.put("first", o3); + Object v2 = map1.get("second"); + Object v3 = map2.get("first"); + Assert.mayAlias(v2, o2); + Assert.notAlias(v2, v3); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/context/collections/HashSet0.java b/sootup.qilin/src/test/java/qilin/microben/context/collections/HashSet0.java new file mode 100644 index 00000000000..bcf1112b1fb --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/collections/HashSet0.java @@ -0,0 +1,40 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.context.collections; + +import java.util.HashSet; +import qilin.microben.utils.Assert; + +// at least 3obj could pass this unit tests. +public class HashSet0 { + public static void main(String[] args) { + HashSet set1 = new HashSet<>(); + HashSet set2 = new HashSet<>(); + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + Object o3 = new Object(); // O3 + set1.add(o1); + set1.add(o2); + set2.add(o3); + Object v2 = set1.iterator().next(); + Object v3 = set2.iterator().next(); + Assert.mayAlias(v2, o1); + Assert.notAlias(v3, v2); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/context/collections/HashTable0.java b/sootup.qilin/src/test/java/qilin/microben/context/collections/HashTable0.java new file mode 100644 index 00000000000..0a52de78112 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/collections/HashTable0.java @@ -0,0 +1,39 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.context.collections; + +import java.util.Hashtable; +import qilin.microben.utils.Assert; + +public class HashTable0 { + public static void main(String[] args) { + Hashtable ht1 = new Hashtable<>(); + Hashtable ht2 = new Hashtable<>(); + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + Object o3 = new Object(); // O3 + ht1.put("first", o1); + ht1.put("second", o2); + ht2.put("first", o3); + Object v1 = ht1.get("first"); + Object v3 = ht2.get("first"); + Assert.mayAlias(v1, o2); + Assert.notAlias(v1, v3); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/context/collections/LinkedList0.java b/sootup.qilin/src/test/java/qilin/microben/context/collections/LinkedList0.java new file mode 100644 index 00000000000..bfd6d84418b --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/collections/LinkedList0.java @@ -0,0 +1,40 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.context.collections; + +import java.util.LinkedList; +import qilin.microben.utils.Assert; + +// need at least 2obj. +public class LinkedList0 { + public static void main(String[] args) { + LinkedList list1 = new LinkedList<>(); + LinkedList list2 = new LinkedList<>(); + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + Object o3 = new Object(); // O3 + list1.add(o1); + list1.add(o2); + list2.add(o3); + Object v1 = list1.get(1); + Object v2 = list2.get(0); + Assert.mayAlias(v1, o2); + Assert.notAlias(v1, v2); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/context/collections/PriorityQueue0.java b/sootup.qilin/src/test/java/qilin/microben/context/collections/PriorityQueue0.java new file mode 100644 index 00000000000..a16508b3b29 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/collections/PriorityQueue0.java @@ -0,0 +1,39 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.context.collections; + +import java.util.PriorityQueue; +import qilin.microben.utils.Assert; + +public class PriorityQueue0 { + public static void main(String[] args) { + PriorityQueue queue1 = new PriorityQueue<>(); + PriorityQueue queue2 = new PriorityQueue<>(); + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + Object o3 = new Object(); // O3 + queue1.add(o1); + queue1.add(o2); + queue2.add(o3); + Object v1 = queue1.poll(); + Object v3 = queue2.poll(); + Assert.mayAlias(v1, o2); + Assert.notAlias(v1, v3); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/context/collections/Stack0.java b/sootup.qilin/src/test/java/qilin/microben/context/collections/Stack0.java new file mode 100644 index 00000000000..66da935c390 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/collections/Stack0.java @@ -0,0 +1,39 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.context.collections; + +import java.util.Stack; +import qilin.microben.utils.Assert; + +public class Stack0 { + public static void main(String[] args) { + Stack s1 = new Stack<>(); + Stack s2 = new Stack<>(); + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + Object o3 = new Object(); // O3 + s1.push(o1); + s2.push(o2); + s2.push(o3); + Object v1 = s1.pop(); + Object v2 = s2.pop(); + Assert.mayAlias(v1, o1); + Assert.notAlias(v1, v2); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/context/collections/TreeMap0.java b/sootup.qilin/src/test/java/qilin/microben/context/collections/TreeMap0.java new file mode 100644 index 00000000000..54d98bfe20b --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/collections/TreeMap0.java @@ -0,0 +1,40 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.context.collections; + +import java.util.Map; +import java.util.TreeMap; +import qilin.microben.utils.Assert; + +public class TreeMap0 { + public static void main(String[] args) { + Map map1 = new TreeMap<>(); + Map map2 = new TreeMap<>(); + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + Object o3 = new Object(); // O3 + map1.put("first", o1); + map1.put("second", o2); + map2.put("first", o3); + Object v2 = map1.get("second"); + Object v3 = map2.get("first"); + Assert.mayAlias(v2, o2); + Assert.notAlias(v2, v3); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/context/collections/TreeSet0.java b/sootup.qilin/src/test/java/qilin/microben/context/collections/TreeSet0.java new file mode 100644 index 00000000000..24eb1ed2d9c --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/collections/TreeSet0.java @@ -0,0 +1,39 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.context.collections; + +import java.util.TreeSet; +import qilin.microben.utils.Assert; + +public class TreeSet0 { + public static void main(String[] args) { + TreeSet set1 = new TreeSet<>(); + TreeSet set2 = new TreeSet<>(); + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + Object o3 = new Object(); // O3 + set1.add(o1); + set1.add(o2); + set2.add(o3); + Object v2 = set1.iterator().next(); + Object v3 = set2.iterator().next(); + Assert.mayAlias(v2, o1); + Assert.notAlias(v3, v2); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/context/collections/Vector0.java b/sootup.qilin/src/test/java/qilin/microben/context/collections/Vector0.java new file mode 100644 index 00000000000..2f716080c96 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/collections/Vector0.java @@ -0,0 +1,39 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.context.collections; + +import java.util.Vector; +import qilin.microben.utils.Assert; + +public class Vector0 { + public static void main(String[] args) { + Vector list1 = new Vector<>(); + Vector list2 = new Vector<>(); + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + Object o3 = new Object(); // O3 + list1.add(o1); + list1.add(o2); + list2.add(o3); + Object v1 = list1.get(1); + Object v2 = list2.get(0); + Assert.mayAlias(v1, o2); + Assert.notAlias(v1, v2); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/context/collections/log.txt b/sootup.qilin/src/test/java/qilin/microben/context/collections/log.txt new file mode 100644 index 00000000000..b98da874382 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/collections/log.txt @@ -0,0 +1,4 @@ +insens-qilin.core: 7 passed, 1 failed. +e-2o: 7 passed, 1 failed. +j-ci: 7 passed, 1 failed. +m-ci: 7 passed, 1 failed. \ No newline at end of file diff --git a/sootup.qilin/src/test/java/qilin/microben/context/hyb/Hyb0.java b/sootup.qilin/src/test/java/qilin/microben/context/hyb/Hyb0.java new file mode 100644 index 00000000000..1c956e866dd --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/hyb/Hyb0.java @@ -0,0 +1,38 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.context.hyb; + +import qilin.microben.utils.Assert; + +public class Hyb0 { + static class A { + static Object id(Object p) { + return p; + } + } + + public static void main(String[] args) { + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + Object v1 = A.id(o1); + Object v2 = A.id(o2); + Assert.mayAlias(v1, o1); + Assert.notAlias(v1, v2); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/context/hyb/Hyb1.java b/sootup.qilin/src/test/java/qilin/microben/context/hyb/Hyb1.java new file mode 100644 index 00000000000..04df489a986 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/hyb/Hyb1.java @@ -0,0 +1,47 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.context.hyb; + +import qilin.microben.utils.Assert; + +public class Hyb1 { + static Object id(Object p) { + return p; + } + + static class A { + Object wid(Object q) { + return id(q); + } + + Object xid(Object r) { + return id(r); + } + } + + public static void main(String[] args) { + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + A a = new A(); + Object v1 = a.wid(o1); + Object v2 = a.xid(o2); + Assert.mayAlias(v1, o1); + Assert.notAlias(v1, v2); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/context/hyb/Hyb2.java b/sootup.qilin/src/test/java/qilin/microben/context/hyb/Hyb2.java new file mode 100644 index 00000000000..dd8c1531222 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/hyb/Hyb2.java @@ -0,0 +1,52 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.context.hyb; + +import qilin.microben.utils.Assert; + +public class Hyb2 { + static class A { + Object f; + } + + static class B { + A foo(Object p) { + A a = create(); + a.f = p; + return a; + } + } + + static A create() { + return new A(); + } + + public static void main(String[] args) { + B b1 = new B(); // B1; + B b2 = new B(); // B2; + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + A a1 = b1.foo(o1); + A a2 = b2.foo(o2); + Object v1 = a1.f; + Object v2 = a2.f; + Assert.mayAlias(v1, o1); + Assert.notAlias(v1, v2); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/context/obj/OBJ1k0.java b/sootup.qilin/src/test/java/qilin/microben/context/obj/OBJ1k0.java new file mode 100644 index 00000000000..3c9a56b3b21 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/obj/OBJ1k0.java @@ -0,0 +1,48 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.context.obj; + +import qilin.microben.utils.Assert; + +class OBJ1k0 { + static class A { + Object id(Object a) { + return a; + } + + Object id2(Object a) { + return id(a); + } + + Object id3(Object a) { + return id2(a); + } + } + + public static void main(String[] argv) { + A a1 = new A(); // A1 + A a2 = new A(); // A2 + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + Object v1 = a1.id3(o1); + Object v2 = a2.id3(o2); + Assert.notAlias(v1, v2); + Assert.mayAlias(v1, o1); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/context/obj/OBJ1k1.java b/sootup.qilin/src/test/java/qilin/microben/context/obj/OBJ1k1.java new file mode 100644 index 00000000000..41b7e4efabb --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/obj/OBJ1k1.java @@ -0,0 +1,54 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.context.obj; + +import qilin.microben.utils.Assert; + +public class OBJ1k1 { + static class A { + Object f; + + A(Object p) { + this.f = p; + } + + Object getF() { + return this.f; + } + } + + static class B { + Object getF(A a) { + return a.getF(); + } + } + + public static void main(String[] args) { + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + A a1 = new A(o1); // A1 + A a2 = new A(o2); // A2 + B b1 = new B(); // B1 + B b2 = new B(); // B2 + Object v1 = b1.getF(a1); + Object v2 = b2.getF(a2); + Assert.notAlias(v1, v2); + Assert.mayAlias(v1, o1); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/context/obj/OBJ1k2.java b/sootup.qilin/src/test/java/qilin/microben/context/obj/OBJ1k2.java new file mode 100644 index 00000000000..3571d962689 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/obj/OBJ1k2.java @@ -0,0 +1,48 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.context.obj; + +import qilin.microben.utils.Assert; + +class OBJ1k2 { + static class A { + Object id2(I i, Object a) { + return i.id(a); + } + } + + static class I { + Object id(Object a) { + return a; + } + } + + public static void main(String[] argv) { + A a1 = new A(); // A1 + A a2 = new A(); // A2 + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + I i1 = new I(); // I1 + I i2 = new I(); // I2 + Object v3 = a1.id2(i1, o1); + Object v4 = a2.id2(i2, o2); + Assert.mayAlias(v3, o1); + Assert.notAlias(v3, v4); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/context/obj/OBJ1k3.java b/sootup.qilin/src/test/java/qilin/microben/context/obj/OBJ1k3.java new file mode 100644 index 00000000000..8b0d078c7bd --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/obj/OBJ1k3.java @@ -0,0 +1,38 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.context.obj; + +import qilin.microben.utils.Assert; + +public class OBJ1k3 { + static class A { + Object id(Object p) { + return p; + } + } + + static A f = new A(); + static A g = new A(); + + public static void main(String[] args) { + Object v1 = f.id(new Object()); + Object v2 = g.id(new Object()); + Assert.notAlias(v1, v2); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/context/obj/OBJ1k4.java b/sootup.qilin/src/test/java/qilin/microben/context/obj/OBJ1k4.java new file mode 100644 index 00000000000..31734ac170e --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/obj/OBJ1k4.java @@ -0,0 +1,48 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.context.obj; + +import qilin.microben.utils.Assert; + +public class OBJ1k4 { + static class A { + Object f; + + public Object getF() { + return f; + } + + public void setF(Object f) { + this.f = f; + } + } + + public static void main(String[] args) { + A a1 = new A(); + A a2 = new A(); + Object o1 = new Object(); + Object o2 = new Object(); + a1.setF(o1); + a2.setF(o2); + Object v1 = a1.getF(); + Object v2 = a2.getF(); + Assert.notAlias(v1, v2); + Assert.mayAlias(v1, o1); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/context/obj/OBJ1k5.java b/sootup.qilin/src/test/java/qilin/microben/context/obj/OBJ1k5.java new file mode 100644 index 00000000000..46e68d2cb90 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/obj/OBJ1k5.java @@ -0,0 +1,45 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.context.obj; + +import qilin.microben.utils.Assert; + +public class OBJ1k5 { + static Object id(Object p) { + return p; + } + + static class A { + public Object foo() { + return id(new Object()); + } + + public Object bar() { + return id(new Object()); + } + } + + public static void main(String[] args) { + A a1 = new A(); + A a2 = new A(); + Object v1 = a1.foo(); + Object v2 = a2.bar(); + Assert.notAlias(v1, v2); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/context/obj/OBJ2k0.java b/sootup.qilin/src/test/java/qilin/microben/context/obj/OBJ2k0.java new file mode 100644 index 00000000000..a7b555f5803 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/obj/OBJ2k0.java @@ -0,0 +1,47 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.context.obj; + +import qilin.microben.utils.Assert; + +public class OBJ2k0 { + static class A { + Object foo(Object o) { + B b = new B(); + return b.id(o); + } + } + + static class B { + Object id(Object o) { + return o; + } + } + + public static void main(String[] args) { + A a1 = new A(); // A1 + A a2 = new A(); // A2 + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + Object v1 = a1.foo(o1); + Object v2 = a2.foo(o2); + Assert.notAlias(v1, v2); + Assert.mayAlias(v1, o1); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/context/obj/OBJ2k1.java b/sootup.qilin/src/test/java/qilin/microben/context/obj/OBJ2k1.java new file mode 100644 index 00000000000..78ef7468fef --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/obj/OBJ2k1.java @@ -0,0 +1,48 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.context.obj; + +import qilin.microben.utils.Assert; + +public class OBJ2k1 { + static class B { + Object f; + } + + static class A { + B create() { + return new B(); // B + } + } + + public static void main(String[] args) { + A a1 = new A(); // A1 + A a2 = new A(); // A2 + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + B b1 = a1.create(); + B b2 = a2.create(); + b1.f = o1; + b2.f = o2; + Object w1 = b1.f; + Object w2 = b2.f; + Assert.notAlias(w1, w2); + Assert.mayAlias(w1, o1); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/context/obj/OBJ2k2.java b/sootup.qilin/src/test/java/qilin/microben/context/obj/OBJ2k2.java new file mode 100644 index 00000000000..c7a3de54bdb --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/obj/OBJ2k2.java @@ -0,0 +1,54 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.context.obj; + +import qilin.microben.utils.Assert; + +public class OBJ2k2 { + static class A { + Object f; + + A(Object p) { + this.f = p; + } + + Object getF() { + return this.f; + } + } + + static class B { + A create(Object q) { + return new A(q); + } + } + + public static void main(String[] args) { + B b1 = new B(); + B b2 = new B(); + Object o1 = new Object(); + Object o2 = new Object(); + A a1 = b1.create(o1); + A a2 = b2.create(o2); + Object v1 = a1.getF(); + Object v2 = a2.getF(); + Assert.mayAlias(v1, o1); + Assert.notAlias(v1, v2); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/context/obj/log.txt b/sootup.qilin/src/test/java/qilin/microben/context/obj/log.txt new file mode 100644 index 00000000000..bbbd5b7cf91 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/obj/log.txt @@ -0,0 +1,5 @@ +insens-qilin.core: 12 passed, 17 failed. +j-ci: 12 passed, 17 failed. +m-ci: 12 passed, 17 failed. +e-2o: 29 passed. +2c: 26 passed, 3 failed. \ No newline at end of file diff --git a/sootup.qilin/src/test/java/qilin/microben/context/type/Type1k0.java b/sootup.qilin/src/test/java/qilin/microben/context/type/Type1k0.java new file mode 100644 index 00000000000..897e7ae0bf0 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/type/Type1k0.java @@ -0,0 +1,57 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.context.type; + +import qilin.microben.utils.Assert; + +/* + * 1Type uses declaring type, not the reciever type. + * */ +public class Type1k0 { + static class A { + Object id(Object p) { + return p; + } + } + + static class B { + A create() { + return new A(); // A1 + } + } + + static class C { + A create() { + return new A(); // A2 + } + } + + public static void main(String[] args) { + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + B b = new B(); // B + C c = new C(); // C + A a1 = b.create(); + A a2 = c.create(); + Object v1 = a1.id(o1); + Object v2 = a2.id(o2); + Assert.mayAlias(v1, o1); + Assert.notAlias(v1, v2); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/context/type/Type2k0.java b/sootup.qilin/src/test/java/qilin/microben/context/type/Type2k0.java new file mode 100644 index 00000000000..f035a82cd3f --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/context/type/Type2k0.java @@ -0,0 +1,66 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.context.type; + +import qilin.microben.utils.Assert; + +public class Type2k0 { + static class A { + Object id(Object q) { + return q; + } + } + + static class B { + A create1() { + return new A(); + } + + A create2() { + return new A(); + } + } + + static class C { + Object foo(Object p) { + B b1 = new B(); // B1 + A a1 = b1.create1(); + return a1.id(p); + } + } + + static class D { + Object bar(Object o) { + B b2 = new B(); // B2 + A a2 = b2.create2(); + return a2.id(o); + } + } + + public static void main(String[] args) { + C c = new C(); + D d = new D(); + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + Object v1 = c.foo(o1); + Object v2 = d.bar(o2); + Assert.mayAlias(v1, o1); + Assert.notAlias(v1, v2); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/array/ArrayCopy.java b/sootup.qilin/src/test/java/qilin/microben/core/array/ArrayCopy.java new file mode 100644 index 00000000000..b960527ae8e --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/array/ArrayCopy.java @@ -0,0 +1,35 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.array; + +import qilin.microben.utils.Assert; + +public class ArrayCopy { + public static void main(String[] ps) { + Object[] xs = new Object[5]; + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + xs[0] = o1; + xs[1] = o2; + Object[] ys = new Object[5]; + System.arraycopy(xs, 0, ys, 0, 2); + Assert.mayAlias(o1, ys[0]); + Assert.mayAlias(o2, xs[0]); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/array/ArrayElemTypeFiltering.java b/sootup.qilin/src/test/java/qilin/microben/core/array/ArrayElemTypeFiltering.java new file mode 100644 index 00000000000..0cfbe8027ed --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/array/ArrayElemTypeFiltering.java @@ -0,0 +1,46 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.array; + +import qilin.microben.utils.Assert; + +public class ArrayElemTypeFiltering { + interface I {} + + static class G implements I {} + + static class H implements I {} + + public static void main(String[] ps) { + I[] array = new G[10]; + G g = new G(); + H h = new H(); + array[0] = g; + array[1] = h; + I i = array[0]; + Assert.notAlias(i, h); + Assert.mayAlias(i, g); + I[] array2 = new I[10]; + array2[0] = g; + array2[1] = h; + I i2 = array2[0]; + Assert.mayAlias(i2, h); + Assert.mayAlias(i2, g); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/array/ArrayIndex.java b/sootup.qilin/src/test/java/qilin/microben/core/array/ArrayIndex.java new file mode 100644 index 00000000000..e9b7099ed55 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/array/ArrayIndex.java @@ -0,0 +1,36 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.array; + +import qilin.microben.utils.Assert; + +public class ArrayIndex { + public static void main(String[] ps) { + Object[] t = new Object[5]; + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + t[0] = o1; + t[1] = o2; + Assert.mayAlias(t[1], o1); + /* + * all array elements are collapsed as one field, i.e., arr. + * */ + Assert.mayAlias(t[1], o2); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/array/MultiArrayComplex.java b/sootup.qilin/src/test/java/qilin/microben/core/array/MultiArrayComplex.java new file mode 100644 index 00000000000..d8abc9f3387 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/array/MultiArrayComplex.java @@ -0,0 +1,38 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.array; + +import qilin.microben.utils.Assert; + +public class MultiArrayComplex { + public static void main(String[] ps) { + Object[][][] xs = new Object[5][5][5]; + Object[] subarray = new Object[5]; + xs[1][1] = subarray; + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + xs[1][1][1] = o1; + subarray[1] = o2; + /* + * current pointer analyses do not distinguish array index. + * */ + Assert.mayAlias(subarray[1], o1); + Assert.mayAlias(xs[1][1][1], o2); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/array/MultiArraySimple.java b/sootup.qilin/src/test/java/qilin/microben/core/array/MultiArraySimple.java new file mode 100644 index 00000000000..9e5e700c686 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/array/MultiArraySimple.java @@ -0,0 +1,30 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.array; + +import qilin.microben.utils.Assert; + +public class MultiArraySimple { + public static void main(String[] ps) { + Object[][][] xs = new Object[5][5][5]; + Object o = new Object(); + xs[1][1][1] = o; + Assert.mayAlias(xs[1][1][1], o); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/assign/CastFail.java b/sootup.qilin/src/test/java/qilin/microben/core/assign/CastFail.java new file mode 100644 index 00000000000..57d4d554cb4 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/assign/CastFail.java @@ -0,0 +1,32 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.assign; + +import qilin.microben.utils.Assert; + +public class CastFail { + public static void main(String[] ps) { + castfail(5); + } + + public static void castfail(Object o) { + String s = (String) o; + Assert.notAlias(s, o); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/assign/CastSucc.java b/sootup.qilin/src/test/java/qilin/microben/core/assign/CastSucc.java new file mode 100644 index 00000000000..197db9245e8 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/assign/CastSucc.java @@ -0,0 +1,29 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.assign; + +import qilin.microben.utils.Assert; + +public class CastSucc { + public static void main(String[] ps) { + Object o = "Hi"; + String s = (String) o; + Assert.mayAlias(s, o); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/assign/InterAssign.java b/sootup.qilin/src/test/java/qilin/microben/core/assign/InterAssign.java new file mode 100644 index 00000000000..3fc42bab09c --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/assign/InterAssign.java @@ -0,0 +1,43 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.assign; + +import qilin.microben.utils.Assert; + +public class InterAssign { + static class A { + Object f; + } + + public static void main(String[] args) { + A a = new A(); + A b = new A(); + b.f = new Object(); + InterAssign m2 = new InterAssign(); + m2.alloc(a, b); + + Object x = a.f; + Object y = b.f; + Assert.mayAlias(x, y); + } + + public void alloc(A x, A y) { + x.f = y.f; + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/assign/NullPointer.java b/sootup.qilin/src/test/java/qilin/microben/core/assign/NullPointer.java new file mode 100644 index 00000000000..badf7e88aab --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/assign/NullPointer.java @@ -0,0 +1,37 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.assign; + +import qilin.microben.utils.Assert; + +/* + * Currently, null is not a pointer value. As a result, the points-to set of both a and b are empty. + * Thus, they are not alias. + * */ +public class NullPointer { + public static void main(String[] args) { + Object a = getNull(); + Object b = a; + Assert.notAlias(b, a); + } + + static Object getNull() { + return null; + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/assign/Receiver2This.java b/sootup.qilin/src/test/java/qilin/microben/core/assign/Receiver2This.java new file mode 100644 index 00000000000..7b18fd6e41e --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/assign/Receiver2This.java @@ -0,0 +1,50 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.assign; + +import qilin.microben.utils.Assert; + +/* + * Note that there is no explicit PAG edge from receiver variable to this^{callee} due to the virtual dispatch. + * */ +public class Receiver2This { + static class A { + void foo(Object o) { + Assert.notAlias(this, o); + } + } + + static class B extends A { + void foo(Object o) { + Assert.mayAlias(this, o); + } + } + + static void test(A a, Object b) { + a.foo(b); + } + + public static void main(String[] args) { + A a1 = new A(); // A + A a2 = new B(); // B + a1.foo(a2); + test(a1, a2); + test(a2, a2); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/assign/Recursion.java b/sootup.qilin/src/test/java/qilin/microben/core/assign/Recursion.java new file mode 100644 index 00000000000..232ff06ddfc --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/assign/Recursion.java @@ -0,0 +1,38 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.assign; + +import qilin.microben.utils.Assert; + +public class Recursion { + static class A { + A next = new A(); + + void foo() { + this.next.foo(); + A nn = this.next.next; + Assert.mayAlias(nn, this); + } + } + + public static void main(String[] args) { + A a = new A(); + a.foo(); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/assign/ReturnValue0.java b/sootup.qilin/src/test/java/qilin/microben/core/assign/ReturnValue0.java new file mode 100644 index 00000000000..bfcee1e5ff5 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/assign/ReturnValue0.java @@ -0,0 +1,35 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.assign; + +import qilin.microben.utils.Assert; + +public class ReturnValue0 { + static Object a, b; + + static Object foo() { + a = new Object(); + return a; + } + + public static void main(String[] args) { + b = foo(); + Assert.mayAlias(a, b); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/assign/ReturnValue1.java b/sootup.qilin/src/test/java/qilin/microben/core/assign/ReturnValue1.java new file mode 100644 index 00000000000..a507a432bf3 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/assign/ReturnValue1.java @@ -0,0 +1,33 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.assign; + +import qilin.microben.utils.Assert; + +public class ReturnValue1 { + public static Object id(Object x) { + return x; + } + + public static void main(String[] args) { + Object a = new Object(); // O1 + Object b = id(a); + Assert.mayAlias(a, b); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/assign/ReturnValue2.java b/sootup.qilin/src/test/java/qilin/microben/core/assign/ReturnValue2.java new file mode 100644 index 00000000000..06b593326ef --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/assign/ReturnValue2.java @@ -0,0 +1,34 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.assign; + +import qilin.microben.utils.Assert; + +public class ReturnValue2 { + public static void main(String[] args) { + Object a = new Object(); + ReturnValue2 x = new ReturnValue2(); + Object v = x.id(a); + Assert.mayAlias(a, v); + } + + public Object id(Object p) { + return p; + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/assign/ReturnValue3.java b/sootup.qilin/src/test/java/qilin/microben/core/assign/ReturnValue3.java new file mode 100644 index 00000000000..8608573a266 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/assign/ReturnValue3.java @@ -0,0 +1,40 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.assign; + +import qilin.microben.utils.Assert; + +public class ReturnValue3 { + static class A { + Object f; + } + + public static Object id(A x) { + Object b = new Object(); + x.f = b; + return b; + } + + public static void main(String[] args) { + A a = new A(); + Object b = id(a); + Object x = a.f; + Assert.mayAlias(b, x); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/assign/SimpleAssign.java b/sootup.qilin/src/test/java/qilin/microben/core/assign/SimpleAssign.java new file mode 100644 index 00000000000..713f07cf4ff --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/assign/SimpleAssign.java @@ -0,0 +1,29 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.assign; + +import qilin.microben.utils.Assert; + +public class SimpleAssign { + public static void main(String[] argv) { + Object a = new Object(); + Object b = a; + Assert.mayAlias(a, b); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/assign/StaticParameter.java b/sootup.qilin/src/test/java/qilin/microben/core/assign/StaticParameter.java new file mode 100644 index 00000000000..b2dbe2b1a98 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/assign/StaticParameter.java @@ -0,0 +1,34 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.assign; + +import qilin.microben.utils.Assert; + +public class StaticParameter { + static Object o; + + static void foo(Object a) { + Assert.mayAlias(a, o); + } + + public static void main(String[] args) { + o = new Object(); + foo(o); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/call/StaticCall.java b/sootup.qilin/src/test/java/qilin/microben/core/call/StaticCall.java new file mode 100644 index 00000000000..58076fb37ed --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/call/StaticCall.java @@ -0,0 +1,36 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.call; + +import qilin.microben.utils.Assert; + +public class StaticCall { + static void foo() { + bar(); + } + + static void bar() { + Object o = new Object(); + Assert.mayAlias(o, o); + } + + public static void main(String[] args) { + foo(); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/call/VirtualCall0.java b/sootup.qilin/src/test/java/qilin/microben/core/call/VirtualCall0.java new file mode 100644 index 00000000000..667950f48e2 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/call/VirtualCall0.java @@ -0,0 +1,51 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.call; + +import qilin.microben.utils.Assert; + +/* + * To check whether virtual dispatch is correct. + * */ +public class VirtualCall0 { + interface IA { + void foo(Object p); + } + + static class A1 implements IA { + @Override + public void foo(Object p) { + Assert.notAlias(this, p); + } + } + + static class A2 implements IA { + @Override + public void foo(Object p) { + Assert.mayAlias(this, p); + } + } + + public static void main(String[] args) { + IA a1 = new A1(); + IA a2 = new A2(); + a1.foo(a2); + a2.foo(a2); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/call/VirtualCall1.java b/sootup.qilin/src/test/java/qilin/microben/core/call/VirtualCall1.java new file mode 100644 index 00000000000..684d5ae67e8 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/call/VirtualCall1.java @@ -0,0 +1,37 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.call; + +import qilin.microben.utils.Assert; + +public class VirtualCall1 { + static class A1 { + public void foo(Object p) { + Assert.notAlias(this, p); + } + } + + static class A2 extends A1 {} + + public static void main(String[] args) { + A1 a1 = new A2(); + A1 a2 = new A2(); + a1.foo(a2); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/clinit/ClinitNewExpr.java b/sootup.qilin/src/test/java/qilin/microben/core/clinit/ClinitNewExpr.java new file mode 100644 index 00000000000..a1720ecc0b8 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/clinit/ClinitNewExpr.java @@ -0,0 +1,41 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.clinit; + +import qilin.microben.utils.Assert; + +/* + * When creating Object A, its () will be triggered by JVM. + * When you run this program, it will output "hello" and "hello2". + * */ +public class ClinitNewExpr { + static class A { + public static Object field = new Object(); + + static { + System.out.println("hello"); + Assert.mayAlias(field, field); + } + } + + public static void main(String[] ps) { + A a = new A(); + System.out.println("hello2" + a); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/clinit/ClinitStaticCall.java b/sootup.qilin/src/test/java/qilin/microben/core/clinit/ClinitStaticCall.java new file mode 100644 index 00000000000..fb36745c2c9 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/clinit/ClinitStaticCall.java @@ -0,0 +1,38 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.clinit; + +import qilin.microben.utils.Assert; + +public class ClinitStaticCall { + static class A { + static Object a = new Object(); + + static void foo() {} + + static { + Assert.mayAlias(a, a); + System.out.println("hello"); + } + } + + public static void main(String[] args) { + A.foo(); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/clinit/ClinitStaticLoad.java b/sootup.qilin/src/test/java/qilin/microben/core/clinit/ClinitStaticLoad.java new file mode 100644 index 00000000000..d075b76dde2 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/clinit/ClinitStaticLoad.java @@ -0,0 +1,38 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.clinit; + +import qilin.microben.utils.Assert; + +public class ClinitStaticLoad { + static class A { + static Object o = new Object(); + + static { + Assert.mayAlias(o, o); + System.out.println("hello"); + } + } + + public static void main(String[] args) { + Object o = new Object(); + Object v = A.o; + Assert.notAlias(o, v); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/clinit/ClinitStaticStore.java b/sootup.qilin/src/test/java/qilin/microben/core/clinit/ClinitStaticStore.java new file mode 100644 index 00000000000..49e39f5d3e0 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/clinit/ClinitStaticStore.java @@ -0,0 +1,37 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.clinit; + +import qilin.microben.utils.Assert; + +public class ClinitStaticStore { + static class A { + static Object f; + static Object o = new Object(); + + static { + Assert.notAlias(o, f); + System.out.println("hello"); + } + } + + public static void main(String[] args) { + A.f = new Object(); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/exception/ExceptionChain.java b/sootup.qilin/src/test/java/qilin/microben/core/exception/ExceptionChain.java new file mode 100644 index 00000000000..478bb09f593 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/exception/ExceptionChain.java @@ -0,0 +1,54 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.exception; + +import qilin.microben.utils.Assert; + +public class ExceptionChain { + Exception t1; + Exception t2; + + void foo(int x) { + try { + if (x <= 10) { + try { + t1 = new ClassCastException(); + throw t1; + } catch (ClassCastException ex) { + Assert.mayAlias(t1, ex); + Assert.notAlias(ex, t2); + } + } else { + t2 = new ArithmeticException(); + throw t2; + } + } catch (ArithmeticException e) { + Assert.notAlias(e, t1); + Assert.mayAlias(e, t2); + } catch (Exception ey) { + Assert.notAlias(ey, t1); + Assert.notAlias(ey, t2); + } + } + + public static void main(String[] args) { + ExceptionChain ec = new ExceptionChain(); + ec.foo(args.length); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/exception/MethodThrow.java b/sootup.qilin/src/test/java/qilin/microben/core/exception/MethodThrow.java new file mode 100644 index 00000000000..8da668dd8c0 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/exception/MethodThrow.java @@ -0,0 +1,41 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.exception; + +import qilin.microben.utils.Assert; + +public class MethodThrow { + static class A { + static RuntimeException f; + + void foo() { + f = new RuntimeException(); + throw f; + } + } + + public static void main(String[] args) { + try { + A a = new A(); + a.foo(); + } catch (Exception e) { + Assert.mayAlias(A.f, e); + } + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/exception/SimpleException.java b/sootup.qilin/src/test/java/qilin/microben/core/exception/SimpleException.java new file mode 100644 index 00000000000..c92f9b23649 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/exception/SimpleException.java @@ -0,0 +1,35 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.exception; + +import qilin.microben.utils.Assert; + +public class SimpleException { + static Exception exp = null; + + public static void main(String[] args) { + try { + exp = new RuntimeException(); + throw exp; + } catch (Exception e) { + System.out.println("Hello"); + Assert.mayAlias(exp, e); + } + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/exception/log.txt b/sootup.qilin/src/test/java/qilin/microben/core/exception/log.txt new file mode 100644 index 00000000000..ca693ed5d99 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/exception/log.txt @@ -0,0 +1,4 @@ +insens-qilin.core: 1 passed. +e-2o: 1 passed. +j-ci: 1 passed. +m-ci: 1 passed. \ No newline at end of file diff --git a/sootup.qilin/src/test/java/qilin/microben/core/field/FieldSensitivity1.java b/sootup.qilin/src/test/java/qilin/microben/core/field/FieldSensitivity1.java new file mode 100644 index 00000000000..592a97b00fd --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/field/FieldSensitivity1.java @@ -0,0 +1,41 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.field; + +import qilin.microben.utils.Assert; + +public class FieldSensitivity1 { + static class A { + Object f; + Object g; + + A(Object o1, Object o2) { + this.f = o1; + this.g = o2; + } + } + + public static void main(String[] args) { + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + A a = new A(o1, o2); + Assert.notAlias(a.f, o2); + Assert.mayAlias(a.g, o2); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/field/FieldSensitivity2.java b/sootup.qilin/src/test/java/qilin/microben/core/field/FieldSensitivity2.java new file mode 100644 index 00000000000..3db239b7d03 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/field/FieldSensitivity2.java @@ -0,0 +1,37 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.field; + +import qilin.microben.utils.Assert; + +public class FieldSensitivity2 { + static class A { + static Object f; + static Object g; + } + + public static void main(String[] args) { + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + A.f = o1; + A.g = o2; + Assert.notAlias(A.f, o2); + Assert.mayAlias(A.g, o2); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/field/InstanceLoad.java b/sootup.qilin/src/test/java/qilin/microben/core/field/InstanceLoad.java new file mode 100644 index 00000000000..6316a3b139e --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/field/InstanceLoad.java @@ -0,0 +1,31 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.field; + +import qilin.microben.utils.Assert; + +public class InstanceLoad { + Object a = new Object(); + + public static void main(String[] args) { + InstanceLoad o = new InstanceLoad(); + Object a = o.a; + Assert.mayAlias(a, o.a); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/field/InstanceStore.java b/sootup.qilin/src/test/java/qilin/microben/core/field/InstanceStore.java new file mode 100644 index 00000000000..9cc8b695374 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/field/InstanceStore.java @@ -0,0 +1,32 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.field; + +import qilin.microben.utils.Assert; + +public class InstanceStore { + Object a; + + public static void main(String[] args) { + InstanceStore i = new InstanceStore(); + Object o = new Object(); + i.a = o; + Assert.mayAlias(i.a, o); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/global/StaticLoad.java b/sootup.qilin/src/test/java/qilin/microben/core/global/StaticLoad.java new file mode 100644 index 00000000000..54531a21b58 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/global/StaticLoad.java @@ -0,0 +1,30 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.global; + +import qilin.microben.utils.Assert; + +public class StaticLoad { + static Object a = new Object(); + + public static void main(String[] args) { + Object b = a; + Assert.mayAlias(a, b); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/global/StaticStore.java b/sootup.qilin/src/test/java/qilin/microben/core/global/StaticStore.java new file mode 100644 index 00000000000..5ac64abc56e --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/global/StaticStore.java @@ -0,0 +1,31 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.global; + +import qilin.microben.utils.Assert; + +public class StaticStore { + static Object a; + + public static void main(String[] args) { + Object o = new Object(); + a = o; + Assert.mayAlias(a, o); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/global/StringConstant.java b/sootup.qilin/src/test/java/qilin/microben/core/global/StringConstant.java new file mode 100644 index 00000000000..9a21fb4c188 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/global/StringConstant.java @@ -0,0 +1,31 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.global; + +import qilin.microben.utils.Assert; + +public class StringConstant { + public static void main(String[] argv) { + foo("abc"); + } + + static void foo(String s) { + Assert.mayAlias(s, "abc"); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/log.txt b/sootup.qilin/src/test/java/qilin/microben/core/log.txt new file mode 100644 index 00000000000..db376d0ed58 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/log.txt @@ -0,0 +1,4 @@ +e-2o: 54 passed. +insens-qilin.core: 54 passed. +m-ci: 54 passed. +j-ci: 53 passed, 1 failed. \ No newline at end of file diff --git a/sootup.qilin/src/test/java/qilin/microben/core/natives/ArrayCopy.java b/sootup.qilin/src/test/java/qilin/microben/core/natives/ArrayCopy.java new file mode 100644 index 00000000000..a208fc4b4f0 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/natives/ArrayCopy.java @@ -0,0 +1,29 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.natives; + +import qilin.microben.utils.Assert; + +public class ArrayCopy { + public static void main(String[] args) { + Object[] src = new Object[] {new Object()}, dest = new Object[1]; + System.arraycopy(src, 0, dest, 0, 1); + Assert.mayAlias(src[0], dest[0]); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/natives/CurrentThread.java b/sootup.qilin/src/test/java/qilin/microben/core/natives/CurrentThread.java new file mode 100644 index 00000000000..e651f73f24b --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/natives/CurrentThread.java @@ -0,0 +1,59 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.natives; + +import qilin.microben.utils.Assert; + +public class CurrentThread { + static Object t; + + static class A extends Thread { + Object f; + + A(Object p) { + this.f = p; + } + + @Override + public void run() { + Assert.mayAlias(f, t); + bar(); + } + + public void bar() { + Thread thr = Thread.currentThread(); + Assert.mayAlias(thr, this); + System.out.println(thr + ";;" + this); + } + } + + public static void main(String[] args) { + Object o = new Object(); + A a = new A(o); + t = o; + a.start(); // run() will be invoked indirectly. + Thread thr = Thread.currentThread(); + /* + * Currently, the currentThread will point to all runnable thread whose run() is invoked. + * It could not distinguish the exact current thread. + * */ + Assert.notAlias(thr, a); + System.out.println(thr + ";;" + a); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/natives/Finalize.java b/sootup.qilin/src/test/java/qilin/microben/core/natives/Finalize.java new file mode 100644 index 00000000000..fdbcbc84a5a --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/natives/Finalize.java @@ -0,0 +1,37 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.natives; + +import qilin.microben.utils.Assert; + +/* + * finalize() is invoked by JVM indirectly. + * */ +public class Finalize { + + @Override + protected void finalize() { + Object o = new Object(); + Assert.mayAlias(o, o); + } + + public static void main(String[] args) { + new Finalize(); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/natives/ObjectClone.java b/sootup.qilin/src/test/java/qilin/microben/core/natives/ObjectClone.java new file mode 100644 index 00000000000..743944fe353 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/natives/ObjectClone.java @@ -0,0 +1,29 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.natives; + +import qilin.microben.utils.Assert; + +public class ObjectClone { + public static void main(String[] args) throws CloneNotSupportedException { + ObjectClone a = new ObjectClone(); + Object b = a.clone(); + Assert.mayAlias(a, b); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/natives/PrivilegedActions0.java b/sootup.qilin/src/test/java/qilin/microben/core/natives/PrivilegedActions0.java new file mode 100644 index 00000000000..f2dbbb92499 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/natives/PrivilegedActions0.java @@ -0,0 +1,39 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.natives; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import qilin.microben.utils.Assert; + +public class PrivilegedActions0 { + public static Object o1; + + public static void main(String[] ps) { + Object t1 = AccessController.doPrivileged(new A()); + Assert.mayAlias(t1, o1); + } + + static class A implements PrivilegedAction { + public Object run() { + o1 = new Object(); + return o1; + } + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/natives/PrivilegedActions1.java b/sootup.qilin/src/test/java/qilin/microben/core/natives/PrivilegedActions1.java new file mode 100644 index 00000000000..d0e9b3d6c61 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/natives/PrivilegedActions1.java @@ -0,0 +1,41 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.natives; + +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedAction; +import qilin.microben.utils.Assert; + +public class PrivilegedActions1 { + public static Object o1; + + public static void main(String[] ps) { + AccessControlContext acc = AccessController.getContext(); + Object t1 = AccessController.doPrivileged(new A(), acc); + Assert.mayAlias(o1, t1); + } + + static class A implements PrivilegedAction { + public Object run() { + o1 = new Object(); + return o1; + } + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/natives/PrivilegedActions2.java b/sootup.qilin/src/test/java/qilin/microben/core/natives/PrivilegedActions2.java new file mode 100644 index 00000000000..d1bbdc803fa --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/natives/PrivilegedActions2.java @@ -0,0 +1,53 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.natives; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import qilin.microben.utils.Assert; + +public class PrivilegedActions2 { + + public static void main(String[] args) { + PrivilegedActions2 p1 = new PrivilegedActions2(); + PrivilegedActions2 p2 = new PrivilegedActions2(); + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + Object v1 = p1.getSystemProperty(o1); + Object v2 = p2.getSystemProperty(o2); + Assert.notAlias(v1, v2); + Assert.mayAlias(o1, v1); + } + + Object getSystemProperty(final Object propName) { + return AccessController.doPrivileged(new MyPrivilegedAction(propName)); + } + + static class MyPrivilegedAction implements PrivilegedAction { + Object propName; + + public MyPrivilegedAction(Object propName) { + this.propName = propName; + } + + public Object run() { + return propName; + } + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/natives/RefArrayGet.java b/sootup.qilin/src/test/java/qilin/microben/core/natives/RefArrayGet.java new file mode 100644 index 00000000000..50db22d8106 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/natives/RefArrayGet.java @@ -0,0 +1,35 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.natives; + +import java.lang.reflect.Array; +import qilin.microben.utils.Assert; + +public class RefArrayGet { + public static void main(String[] args) { + Object[] data = new Object[10]; + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + data[0] = o1; + Object v1 = Array.get(data, 0); + System.out.println(o1 + ";;" + v1); + Assert.mayAlias(o1, v1); + Assert.notAlias(o2, v1); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/natives/RefArraySet.java b/sootup.qilin/src/test/java/qilin/microben/core/natives/RefArraySet.java new file mode 100644 index 00000000000..9ee22e781c4 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/natives/RefArraySet.java @@ -0,0 +1,35 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.natives; + +import java.lang.reflect.Array; +import qilin.microben.utils.Assert; + +public class RefArraySet { + public static void main(String[] args) { + Object[] data = new Object[10]; + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + Array.set(data, 0, o1); + Object v1 = data[0]; + System.out.println(o1 + ";;" + v1); + Assert.mayAlias(o1, v1); + Assert.notAlias(o2, v1); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/natives/SystemErr.java b/sootup.qilin/src/test/java/qilin/microben/core/natives/SystemErr.java new file mode 100644 index 00000000000..d14bbaf0fcb --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/natives/SystemErr.java @@ -0,0 +1,31 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.natives; + +import java.io.FileNotFoundException; +import java.io.PrintStream; +import qilin.microben.utils.Assert; + +public class SystemErr { + public static void main(String[] args) throws FileNotFoundException { + PrintStream err = new PrintStream(""); + System.setErr(err); + Assert.mayAlias(err, System.err); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/natives/SystemIn.java b/sootup.qilin/src/test/java/qilin/microben/core/natives/SystemIn.java new file mode 100644 index 00000000000..067ca7b1316 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/natives/SystemIn.java @@ -0,0 +1,36 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.natives; + +import java.io.InputStream; +import qilin.microben.utils.Assert; + +public class SystemIn { + public static void main(String[] args) { + InputStream in = + new InputStream() { + @Override + public int read() { + return 0; + } + }; + System.setIn(in); + Assert.mayAlias(in, System.in); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/natives/SystemOut.java b/sootup.qilin/src/test/java/qilin/microben/core/natives/SystemOut.java new file mode 100644 index 00000000000..1221f2800d9 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/natives/SystemOut.java @@ -0,0 +1,31 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.natives; + +import java.io.FileNotFoundException; +import java.io.PrintStream; +import qilin.microben.utils.Assert; + +public class SystemOut { + public static void main(String[] args) throws FileNotFoundException { + PrintStream out = new PrintStream(""); + System.setOut(out); + Assert.mayAlias(out, System.out); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/natives/ThreadRun.java b/sootup.qilin/src/test/java/qilin/microben/core/natives/ThreadRun.java new file mode 100644 index 00000000000..c93cf2a9fb1 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/natives/ThreadRun.java @@ -0,0 +1,45 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.natives; + +import qilin.microben.utils.Assert; + +public class ThreadRun { + static Object t; + + static class A extends Thread { + Object f; + + A(Object p) { + this.f = p; + } + + @Override + public void run() { + Assert.mayAlias(f, t); + } + } + + public static void main(String[] args) { + Object o = new Object(); + A a = new A(o); + t = o; + a.start(); // run() will be invoked indirectly. + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/natives/log.txt b/sootup.qilin/src/test/java/qilin/microben/core/natives/log.txt new file mode 100644 index 00000000000..a17f25e3e97 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/natives/log.txt @@ -0,0 +1,4 @@ +insens-qilin.core: 7 passed. +j-ci: 7 passed. +m-ci: 7 passed. +e-2o: 7 passed. \ No newline at end of file diff --git a/sootup.qilin/src/test/java/qilin/microben/core/reflog/ArrayNewInstance.java b/sootup.qilin/src/test/java/qilin/microben/core/reflog/ArrayNewInstance.java new file mode 100644 index 00000000000..f4cd0797c4d --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/reflog/ArrayNewInstance.java @@ -0,0 +1,30 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.reflog; + +import java.lang.reflect.Array; +import qilin.microben.utils.Assert; + +public class ArrayNewInstance { + public static void main(String[] args) { + Object array = Array.newInstance(Integer.TYPE, 10); + int[] arrayCast = (int[]) array; + Assert.mayAlias(array, arrayCast); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/reflog/ArrayNewInstance.log b/sootup.qilin/src/test/java/qilin/microben/core/reflog/ArrayNewInstance.log new file mode 100644 index 00000000000..8287576b4c5 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/reflog/ArrayNewInstance.log @@ -0,0 +1 @@ +Array.newInstance;int[];qilin.microben.core.reflog.ArrayNewInstance.main;9;; \ No newline at end of file diff --git a/sootup.qilin/src/test/java/qilin/microben/core/reflog/ClassForName.java b/sootup.qilin/src/test/java/qilin/microben/core/reflog/ClassForName.java new file mode 100644 index 00000000000..f92aa0bd906 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/reflog/ClassForName.java @@ -0,0 +1,34 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.reflog; + +import qilin.microben.utils.Assert; + +public class ClassForName { + public static void main(String[] args) throws Exception { + String realClassName = "qilin.microben.core.reflog.ClassForName"; + Class c = Class.forName(realClassName); + String realClassName2 = "qilin.microben.core.reflog.ClassForName"; + Class c2 = Class.forName(realClassName2); + String realClassName3 = "java.lang.Object"; + Class c3 = Class.forName(realClassName3); + Assert.mayAlias(c, c2); + Assert.notAlias(c, c3); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/reflog/ClassForName.log b/sootup.qilin/src/test/java/qilin/microben/core/reflog/ClassForName.log new file mode 100644 index 00000000000..e921273ac15 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/reflog/ClassForName.log @@ -0,0 +1,3 @@ +Class.forName;qilin.microben.core.reflog.ClassForName;qilin.microben.core.reflog.ClassForName.main;9;; +Class.forName;qilin.microben.core.reflog.ClassForName;qilin.microben.core.reflog.ClassForName.main;11;; +Class.forName;java.lang.Object;qilin.microben.core.reflog.ClassForName.main;13;; \ No newline at end of file diff --git a/sootup.qilin/src/test/java/qilin/microben/core/reflog/ClassForName1.java b/sootup.qilin/src/test/java/qilin/microben/core/reflog/ClassForName1.java new file mode 100644 index 00000000000..c029731526f --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/reflog/ClassForName1.java @@ -0,0 +1,34 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.reflog; + +import qilin.microben.utils.Assert; + +public class ClassForName1 { + public static void main(String[] args) throws Exception { + String realClassName = "qilin.microben.core.reflog.ClassForName1"; + Class c = Class.forName(realClassName, true, ClassLoader.getSystemClassLoader()); + String realClassName2 = "qilin.microben.core.reflog.ClassForName1"; + Class c2 = Class.forName(realClassName2, true, ClassLoader.getSystemClassLoader()); + String realClassName3 = "java.lang.Object"; + Class c3 = Class.forName(realClassName3, true, ClassLoader.getSystemClassLoader()); + Assert.mayAlias(c, c2); + Assert.notAlias(c, c3); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/reflog/ClassForName1.log b/sootup.qilin/src/test/java/qilin/microben/core/reflog/ClassForName1.log new file mode 100644 index 00000000000..8bcddb94555 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/reflog/ClassForName1.log @@ -0,0 +1,3 @@ +Class.forName;qilin.microben.core.reflog.ClassForName1;qilin.microben.core.reflog.ClassForName1.main;9;; +Class.forName;qilin.microben.core.reflog.ClassForName1;qilin.microben.core.reflog.ClassForName1.main;11;; +Class.forName;java.lang.Object;qilin.microben.core.reflog.ClassForName1.main;13;; \ No newline at end of file diff --git a/sootup.qilin/src/test/java/qilin/microben/core/reflog/ClassNewInstance.java b/sootup.qilin/src/test/java/qilin/microben/core/reflog/ClassNewInstance.java new file mode 100644 index 00000000000..084c5f9e1af --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/reflog/ClassNewInstance.java @@ -0,0 +1,29 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.reflog; + +import qilin.microben.utils.Assert; + +/** to test, ClassNewInstance.log file should be put under the same directory */ +public class ClassNewInstance { + public static void main(String[] args) throws Exception { + Object a = ClassNewInstance.class.newInstance(); + Assert.mayAlias(a, a); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/reflog/ClassNewInstance.log b/sootup.qilin/src/test/java/qilin/microben/core/reflog/ClassNewInstance.log new file mode 100644 index 00000000000..8c7d38cb11b --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/reflog/ClassNewInstance.log @@ -0,0 +1 @@ +Class.newInstance;qilin.microben.core.reflog.ClassNewInstance;qilin.microben.core.reflog.ClassNewInstance.main;26;; \ No newline at end of file diff --git a/sootup.qilin/src/test/java/qilin/microben/core/reflog/ConstructorNewInstance.java b/sootup.qilin/src/test/java/qilin/microben/core/reflog/ConstructorNewInstance.java new file mode 100644 index 00000000000..e8221f1dab8 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/reflog/ConstructorNewInstance.java @@ -0,0 +1,37 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.reflog; + +import qilin.microben.utils.Assert; + +/** to test, ConstrucorNewInstance.log file should be put under the same directory */ +public class ConstructorNewInstance { + Object f; + + public ConstructorNewInstance(Object f) { + this.f = f; + } + + public static void main(String[] args) throws Exception { + Object f = new Object(); + ConstructorNewInstance a = + ConstructorNewInstance.class.getConstructor(Object.class).newInstance(f); + Assert.mayAlias(f, a.f); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/reflog/ConstructorNewInstance.log b/sootup.qilin/src/test/java/qilin/microben/core/reflog/ConstructorNewInstance.log new file mode 100644 index 00000000000..9b7e5b360cb --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/reflog/ConstructorNewInstance.log @@ -0,0 +1 @@ +Constructor.newInstance;(java.lang.Object)>;qilin.microben.core.reflog.ConstructorNewInstance.main;17;; \ No newline at end of file diff --git a/sootup.qilin/src/test/java/qilin/microben/core/reflog/DoopRefBug.java b/sootup.qilin/src/test/java/qilin/microben/core/reflog/DoopRefBug.java new file mode 100644 index 00000000000..9d9a815a074 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/reflog/DoopRefBug.java @@ -0,0 +1,36 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.reflog; + +import java.lang.reflect.Field; +import javax.xml.namespace.QName; +import javax.xml.xpath.XPathConstants; +import qilin.microben.utils.Assert; + +public class DoopRefBug { + public static void main(String[] args) throws Exception { + String realClassName = "javax.xml.xpath.XPathConstants"; + Class c = Class.forName(realClassName); + String fieldName = "STRING"; + Field f = c.getField(fieldName); + Object o = f.get(null); + QName S = XPathConstants.STRING; + Assert.mayAlias(o, S); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/reflog/DoopRefBug.log b/sootup.qilin/src/test/java/qilin/microben/core/reflog/DoopRefBug.log new file mode 100644 index 00000000000..c5696896f4b --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/reflog/DoopRefBug.log @@ -0,0 +1 @@ +Field.get*;;qilin.microben.core.reflog.DoopRefBug.main;15;; \ No newline at end of file diff --git a/sootup.qilin/src/test/java/qilin/microben/core/reflog/FieldGet.java b/sootup.qilin/src/test/java/qilin/microben/core/reflog/FieldGet.java new file mode 100644 index 00000000000..655ad465803 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/reflog/FieldGet.java @@ -0,0 +1,33 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.reflog; + +import qilin.microben.utils.Assert; + +public class FieldGet { + public Object f; + + public static void main(String[] args) throws Exception { + FieldGet fg = new FieldGet(); + Object c = new Object(); + fg.f = c; + Object d = FieldGet.class.getField("f").get(fg); + Assert.mayAlias(c, d); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/reflog/FieldGet.log b/sootup.qilin/src/test/java/qilin/microben/core/reflog/FieldGet.log new file mode 100644 index 00000000000..aa9f40b9e23 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/reflog/FieldGet.log @@ -0,0 +1 @@ +Field.get*;;qilin.microben.core.reflog.FieldGet.main;12;; \ No newline at end of file diff --git a/sootup.qilin/src/test/java/qilin/microben/core/reflog/FieldGetStatic.java b/sootup.qilin/src/test/java/qilin/microben/core/reflog/FieldGetStatic.java new file mode 100644 index 00000000000..4b0a2a1344e --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/reflog/FieldGetStatic.java @@ -0,0 +1,32 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.reflog; + +import qilin.microben.utils.Assert; + +public class FieldGetStatic { + public static Object f; + + public static void main(String[] args) throws Exception { + Object a = new Object(); + FieldGetStatic.f = a; + Object b = FieldGetStatic.class.getField("f").get(null); + Assert.mayAlias(a, b); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/reflog/FieldGetStatic.log b/sootup.qilin/src/test/java/qilin/microben/core/reflog/FieldGetStatic.log new file mode 100644 index 00000000000..fee1c569a92 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/reflog/FieldGetStatic.log @@ -0,0 +1 @@ +Field.get*;;qilin.microben.core.reflog.FieldGetStatic.main;11;; \ No newline at end of file diff --git a/sootup.qilin/src/test/java/qilin/microben/core/reflog/FieldSet.java b/sootup.qilin/src/test/java/qilin/microben/core/reflog/FieldSet.java new file mode 100644 index 00000000000..7acffe86e9c --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/reflog/FieldSet.java @@ -0,0 +1,37 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.reflog; + +import qilin.microben.utils.Assert; + +public class FieldSet { + public Object f; + + public static void main(String[] args) throws Exception { + FieldSet fs1 = new FieldSet(); + FieldSet fs2 = new FieldSet(); + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + FieldSet.class.getField("f").set(fs1, o1); + FieldSet.class.getField("f").set(fs2, o2); + Object v1 = fs1.f; + Assert.notAlias(o2, v1); + Assert.mayAlias(v1, o1); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/reflog/FieldSet.log b/sootup.qilin/src/test/java/qilin/microben/core/reflog/FieldSet.log new file mode 100644 index 00000000000..1203dc0e3da --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/reflog/FieldSet.log @@ -0,0 +1,2 @@ +Field.set*;;qilin.microben.core.reflog.FieldSet.main;13;; +Field.set*;;qilin.microben.core.reflog.FieldSet.main;14;; \ No newline at end of file diff --git a/sootup.qilin/src/test/java/qilin/microben/core/reflog/FieldSetStatic.java b/sootup.qilin/src/test/java/qilin/microben/core/reflog/FieldSetStatic.java new file mode 100644 index 00000000000..b3aa36ed3b7 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/reflog/FieldSetStatic.java @@ -0,0 +1,32 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.reflog; + +import qilin.microben.utils.Assert; + +public class FieldSetStatic { + public static Object f; + + public static void main(String[] args) throws Exception { + Object a = new Object(); + FieldSetStatic.class.getField("f").set(null, a); + Object b = f; + Assert.mayAlias(a, b); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/reflog/FieldSetStatic.log b/sootup.qilin/src/test/java/qilin/microben/core/reflog/FieldSetStatic.log new file mode 100644 index 00000000000..cdbad95ad4a --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/reflog/FieldSetStatic.log @@ -0,0 +1 @@ +Field.set*;;qilin.microben.core.reflog.FieldSetStatic.main;10;; \ No newline at end of file diff --git a/sootup.qilin/src/test/java/qilin/microben/core/reflog/MethodInvoke.java b/sootup.qilin/src/test/java/qilin/microben/core/reflog/MethodInvoke.java new file mode 100644 index 00000000000..412bd95f85f --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/reflog/MethodInvoke.java @@ -0,0 +1,40 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.reflog; + +import qilin.microben.utils.Assert; + +public class MethodInvoke { + public static void main(String[] args) throws Exception { + Object c = new Object(); + Object d = + MethodInvoke.class.getMethod("id", Object.class).invoke(new MethodInvokeInstance(), c); + Assert.mayAlias(c, d); + } + + public Object id(Object a) { + return null; + } + + static class MethodInvokeInstance extends MethodInvoke { + public Object id(Object a) { + return a; + } + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/reflog/MethodInvoke.log b/sootup.qilin/src/test/java/qilin/microben/core/reflog/MethodInvoke.log new file mode 100644 index 00000000000..00da374aa84 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/reflog/MethodInvoke.log @@ -0,0 +1 @@ +Method.invoke;;qilin.microben.core.reflog.MethodInvoke.main;26;; \ No newline at end of file diff --git a/sootup.qilin/src/test/java/qilin/microben/core/reflog/MethodInvokeStatic.java b/sootup.qilin/src/test/java/qilin/microben/core/reflog/MethodInvokeStatic.java new file mode 100644 index 00000000000..05f0ec63fc3 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/reflog/MethodInvokeStatic.java @@ -0,0 +1,34 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.core.reflog; + +import qilin.microben.utils.Assert; + +/** to test, MethodInvokeStatic.log file should be put under the same directory */ +public class MethodInvokeStatic { + public static Object id(Object a) { + return a; + } + + public static void main(String[] args) throws Exception { + Object a = new Object(); + Object b = MethodInvokeStatic.class.getMethod("id", Object.class).invoke(null, a); + Assert.mayAlias(a, b); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/core/reflog/MethodInvokeStatic.log b/sootup.qilin/src/test/java/qilin/microben/core/reflog/MethodInvokeStatic.log new file mode 100644 index 00000000000..4c3a60d94c6 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/reflog/MethodInvokeStatic.log @@ -0,0 +1 @@ +Method.invoke;;qilin.microben.core.reflog.MethodInvokeStatic.main;31;; \ No newline at end of file diff --git a/sootup.qilin/src/test/java/qilin/microben/core/reflog/Reflection.log b/sootup.qilin/src/test/java/qilin/microben/core/reflog/Reflection.log new file mode 100644 index 00000000000..59ea3c49cda --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/reflog/Reflection.log @@ -0,0 +1,17 @@ +Array.newInstance;int[];qilin.microben.core.reflog.ArrayNewInstance.main;27;; +Class.forName;qilin.microben.core.reflog.ClassForName;qilin.microben.core.reflog.ClassForName.main;27;; +Class.forName;qilin.microben.core.reflog.ClassForName;qilin.microben.core.reflog.ClassForName.main;29;; +Class.forName;java.lang.Object;qilin.microben.core.reflog.ClassForName.main;31;; +Class.forName;qilin.microben.core.reflog.ClassForName1;qilin.microben.core.reflog.ClassForName1.main;27;; +Class.forName;qilin.microben.core.reflog.ClassForName1;qilin.microben.core.reflog.ClassForName1.main;29;; +Class.forName;java.lang.Object;qilin.microben.core.reflog.ClassForName1.main;31;; +Class.newInstance;qilin.microben.core.reflog.ClassNewInstance;qilin.microben.core.reflog.ClassNewInstance.main;28;; +Constructor.newInstance;(java.lang.Object)>;qilin.microben.core.reflog.ConstructorNewInstance.main;35;; +Field.get*;;qilin.microben.core.reflog.DoopRefBug.main;33;; +Field.get*;;qilin.microben.core.reflog.FieldGet.main;30;; +Field.get*;;qilin.microben.core.reflog.FieldGetStatic.main;29;; +Field.set*;;qilin.microben.core.reflog.FieldSet.main;31;; +Field.set*;;qilin.microben.core.reflog.FieldSet.main;32;; +Field.set*;;qilin.microben.core.reflog.FieldSetStatic.main;28;; +Method.invoke;;qilin.microben.core.reflog.MethodInvoke.main;26;; +Method.invoke;;qilin.microben.core.reflog.MethodInvokeStatic.main;33;; \ No newline at end of file diff --git a/sootup.qilin/src/test/java/qilin/microben/core/reflog/log.txt b/sootup.qilin/src/test/java/qilin/microben/core/reflog/log.txt new file mode 100644 index 00000000000..5ebbacf21a9 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/core/reflog/log.txt @@ -0,0 +1 @@ +m-ci: 10 passed. \ No newline at end of file diff --git a/sootup.qilin/src/test/java/qilin/microben/flowsens/Branching1.java b/sootup.qilin/src/test/java/qilin/microben/flowsens/Branching1.java new file mode 100644 index 00000000000..b0e27ad1da2 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/flowsens/Branching1.java @@ -0,0 +1,36 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.flowsens; + +import qilin.microben.utils.Assert; + +/* + * @testcase Branching1 + * @description Condition. a and b alias on one path, not on the other + */ +public class Branching1 { + public static void main(String[] args) { + Object a = new Object(); + Object b = new Object(); + if (args.length > 2) { + a = b; + } + Assert.mayAlias(a, b); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/flowsens/FlowSens0.java b/sootup.qilin/src/test/java/qilin/microben/flowsens/FlowSens0.java new file mode 100644 index 00000000000..48935c1594f --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/flowsens/FlowSens0.java @@ -0,0 +1,45 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.flowsens; + +import qilin.microben.utils.Assert; + +public class FlowSens0 { + static class A { + Object f; + } + + public static void main(String[] args) { + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + Object o3 = new Object(); // O3 + A a1 = new A(); // A1 + A a2 = new A(); // A2 + a1.f = o1; + a2.f = o2; + Object v1 = a1.f; + Object v2 = a2.f; + Assert.mayAlias(v1, o1); + Assert.notAlias(v1, v2); + a1.f = o3; + Object v3 = a1.f; + Assert.mayAlias(v3, o3); + Assert.notAlias(v3, v1); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/flowsens/InstanceOf0.java b/sootup.qilin/src/test/java/qilin/microben/flowsens/InstanceOf0.java new file mode 100644 index 00000000000..23c8c4a8581 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/flowsens/InstanceOf0.java @@ -0,0 +1,44 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.flowsens; + +import qilin.microben.utils.Assert; + +public class InstanceOf0 { + static class A {} + + static class B {} + + static Object o1, o2; + + static void foo(Object p) { + if (p instanceof A) { + Assert.mayAlias(p, o1); + } else { + Assert.notAlias(p, o1); + } + } + + public static void main(String[] args) { + o1 = new A(); + o2 = new B(); + foo(o1); + foo(o2); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/flowsens/Loops.java b/sootup.qilin/src/test/java/qilin/microben/flowsens/Loops.java new file mode 100644 index 00000000000..3f664848d3a --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/flowsens/Loops.java @@ -0,0 +1,43 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.flowsens; + +import qilin.microben.utils.Assert; + +public class Loops { + public static class N { + public N next; + + public N() { + next = new N(); + } + } + + public static void main(String[] args) { + N node = new N(); + int i = 0; + while (i < 10) { + node = node.next; + i++; + } + N o = node.next; + N p = node.next.next; + Assert.mayAlias(o, p); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/flowsens/StrongUpdate1.java b/sootup.qilin/src/test/java/qilin/microben/flowsens/StrongUpdate1.java new file mode 100644 index 00000000000..859703b2434 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/flowsens/StrongUpdate1.java @@ -0,0 +1,39 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.flowsens; + +import qilin.microben.utils.Assert; + +public class StrongUpdate1 { + static class A { + Object f; + } + + public static void main(String[] args) { + A a1 = new A(); + A a2 = a1; + Object o1 = new Object(); + Object o2 = new Object(); + a2.f = o1; + a1.f = o2; + Object v1 = a2.f; + Assert.mayAlias(v1, o2); + Assert.notAlias(v1, o1); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/flowsens/StrongUpdate2.java b/sootup.qilin/src/test/java/qilin/microben/flowsens/StrongUpdate2.java new file mode 100644 index 00000000000..7208ffb6c1b --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/flowsens/StrongUpdate2.java @@ -0,0 +1,43 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.flowsens; + +import qilin.microben.utils.Assert; + +// need flow-sensitivity. +public class StrongUpdate2 { + static StrongUpdate2 v; + Object f, g; + + public static void main(String[] args) { + StrongUpdate2 v1 = new StrongUpdate2(); + StrongUpdate2 v2 = new StrongUpdate2(); + Object o1 = new Object(); // O1 + Object o2 = new Object(); // O2 + Object o3 = new Object(); // O3 + v1.f = o1; + v2.f = o2; + v1.g = o3; + v = v1; + v = v2; // strong update pointer. + v.g = v.f; + Assert.notAlias(v1.g, v2.g); + Assert.mayAlias(v2.g, o2); + } +} diff --git a/sootup.qilin/src/test/java/qilin/microben/flowsens/log.txt b/sootup.qilin/src/test/java/qilin/microben/flowsens/log.txt new file mode 100644 index 00000000000..d2f1521e6eb --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/flowsens/log.txt @@ -0,0 +1,4 @@ +insens-qilin.core: 1 passed, 2 failed. +j-ci: 1 passed, 2 failed. +m-ci: 1 passed, 2 failed. +e-2o: 1 passed, 2 failed. \ No newline at end of file diff --git a/sootup.qilin/src/test/java/qilin/microben/utils/Assert.java b/sootup.qilin/src/test/java/qilin/microben/utils/Assert.java new file mode 100644 index 00000000000..f6c8e17cfa6 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/microben/utils/Assert.java @@ -0,0 +1,34 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.microben.utils; + +public class Assert { + // dummy methods for testee + public static void call(Object... o) { + // to prevent the compiler to prune the Object + } + + public static void mayAlias(Object a, Object b) { + // to be detected by Analyzer + } + + public static void notAlias(Object a, Object b) { + // to be detected by Analyzer + } +} diff --git a/sootup.qilin/src/test/java/qilin/test/FlowSensTests.java b/sootup.qilin/src/test/java/qilin/test/FlowSensTests.java new file mode 100644 index 00000000000..adff75c48d9 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/test/FlowSensTests.java @@ -0,0 +1,55 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.test; + +import org.junit.Ignore; +import org.junit.Test; +import qilin.test.util.JunitTests; + +public class FlowSensTests extends JunitTests { + @Test + public void testLoops() { + checkAssertions(run("qilin.microben.flowsens.Loops")); + } + + @Ignore + public void testFlowSens0() { + checkAssertions(run("qilin.microben.flowsens.FlowSens0")); + } + + @Ignore + public void testInstanceOf0() { + checkAssertions(run("qilin.microben.flowsens.InstanceOf0")); + } + + @Ignore + public void testBranching1() { + checkAssertions(run("qilin.microben.flowsens.Branching1")); + } + + @Ignore + public void testStrongUpdate1() { + checkAssertions(run("qilin.microben.flowsens.StrongUpdate1")); + } + + @Ignore + public void testStrongUpdate2() { + checkAssertions(run("qilin.microben.flowsens.StrongUpdate2")); + } +} diff --git a/sootup.qilin/src/test/java/qilin/test/context/CFATests.java b/sootup.qilin/src/test/java/qilin/test/context/CFATests.java new file mode 100644 index 00000000000..043a155c280 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/test/context/CFATests.java @@ -0,0 +1,45 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.test.context; + +import org.junit.Test; +import qilin.test.util.JunitTests; + +public class CFATests extends JunitTests { + + @Test + public void testCFA1k0() { + checkAssertions(run("qilin.microben.context.cfa.CFA1k0", "1c")); + } + + @Test + public void testCFA1k1() { + checkAssertions(run("qilin.microben.context.cfa.CFA1k1", "1c")); + } + + @Test + public void testCFA1k2() { + checkAssertions(run("qilin.microben.context.cfa.CFA1k2", "1c")); + } + + @Test + public void testCFA2k() { + checkAssertions(run("qilin.microben.context.cfa.CFA2k", "2c")); + } +} diff --git a/sootup.qilin/src/test/java/qilin/test/context/CollectionsTests.java b/sootup.qilin/src/test/java/qilin/test/context/CollectionsTests.java new file mode 100644 index 00000000000..1eb75ca117b --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/test/context/CollectionsTests.java @@ -0,0 +1,74 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.test.context; + +import org.junit.Test; +import qilin.test.util.JunitTests; + +public class CollectionsTests extends JunitTests { + @Test + public void testArrayList0() { + checkAssertions(run("qilin.microben.context.collections.ArrayList0", "2o")); + } + + @Test + public void testLinkedList0() { + checkAssertions(run("qilin.microben.context.collections.LinkedList0", "2o")); + } + + @Test + public void testVector0() { + checkAssertions(run("qilin.microben.context.collections.Vector0", "2o")); + } + + @Test + public void testHashMap0() { + checkAssertions(run("qilin.microben.context.collections.HashMap0", "2o")); + } + + @Test + public void testTreeMap0() { + checkAssertions(run("qilin.microben.context.collections.TreeMap0", "2o")); + } + + @Test + public void testHashSet0() { + checkAssertions(run("qilin.microben.context.collections.HashSet0", "3o")); + } + + @Test + public void testTreeSet0() { + checkAssertions(run("qilin.microben.context.collections.TreeSet0", "3o")); + } + + @Test + public void testHashTable0() { + checkAssertions(run("qilin.microben.context.collections.HashTable0", "2o")); + } + + @Test + public void testPriorityQueue0() { + checkAssertions(run("qilin.microben.context.collections.PriorityQueue0", "2o")); + } + + @Test + public void testStack0() { + checkAssertions(run("qilin.microben.context.collections.Stack0", "2o")); + } +} diff --git a/sootup.qilin/src/test/java/qilin/test/context/HybTests.java b/sootup.qilin/src/test/java/qilin/test/context/HybTests.java new file mode 100644 index 00000000000..7a661ef3408 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/test/context/HybTests.java @@ -0,0 +1,39 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.test.context; + +import org.junit.Test; +import qilin.test.util.JunitTests; + +public class HybTests extends JunitTests { + @Test + public void testHyb0() { + checkAssertions(run("qilin.microben.context.hyb.Hyb0", "1h")); + } + + @Test + public void testHyb1() { + checkAssertions(run("qilin.microben.context.hyb.Hyb1", "1h")); + } + + @Test + public void testHyb2() { + checkAssertions(run("qilin.microben.context.hyb.Hyb2", "2h")); + } +} diff --git a/sootup.qilin/src/test/java/qilin/test/context/OBJTests.java b/sootup.qilin/src/test/java/qilin/test/context/OBJTests.java new file mode 100644 index 00000000000..ffb34279e19 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/test/context/OBJTests.java @@ -0,0 +1,69 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.test.context; + +import org.junit.Test; +import qilin.test.util.JunitTests; + +public class OBJTests extends JunitTests { + @Test + public void testOBJ1k0() { + checkAssertions(run("qilin.microben.context.obj.OBJ1k0", "1o")); + } + + @Test + public void testOBJ1k1() { + checkAssertions(run("qilin.microben.context.obj.OBJ1k1", "1o")); + } + + @Test + public void testOBJ1k2() { + checkAssertions(run("qilin.microben.context.obj.OBJ1k2", "1o")); + } + + @Test + public void testOBJ1k3() { + checkAssertions(run("qilin.microben.context.obj.OBJ1k3", "1o")); + } + + @Test + public void testOBJ1k4() { + checkAssertions(run("qilin.microben.context.obj.OBJ1k4", "1o")); + } + + @Test + public void testOBJ1k5() { + checkAssertions(run("qilin.microben.context.obj.OBJ1k5", "1o")); + } + + @Test + public void testOBJ2k0() { + checkAssertions(run("qilin.microben.context.obj.OBJ2k0", "2o")); + } + + @Test + public void testOBJ2k1() { + checkAssertions(run("qilin.microben.context.obj.OBJ2k1", "2o")); + } + + @Test + public void testOBJ2k2() { + checkAssertions(run("qilin.microben.context.obj.OBJ2k2", "2o")); + } +} diff --git a/sootup.qilin/src/test/java/qilin/test/context/TypeTests.java b/sootup.qilin/src/test/java/qilin/test/context/TypeTests.java new file mode 100644 index 00000000000..9d239007643 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/test/context/TypeTests.java @@ -0,0 +1,34 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.test.context; + +import org.junit.Test; +import qilin.test.util.JunitTests; + +public class TypeTests extends JunitTests { + @Test + public void testType1k0() { + checkAssertions(run("qilin.microben.context.type.Type1k0", "1t")); + } + + @Test + public void testType2k0() { + checkAssertions(run("qilin.microben.context.type.Type2k0", "2t")); + } +} diff --git a/sootup.qilin/src/test/java/qilin/test/core/ArrayTests.java b/sootup.qilin/src/test/java/qilin/test/core/ArrayTests.java new file mode 100644 index 00000000000..213467a4d82 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/test/core/ArrayTests.java @@ -0,0 +1,49 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.test.core; + +import org.junit.Test; +import qilin.test.util.JunitTests; + +public class ArrayTests extends JunitTests { + @Test + public void testArrayIndex() { + checkAssertions(run("qilin.microben.core.array.ArrayIndex")); + } + + @Test + public void testMultiArraySimple() { + checkAssertions(run("qilin.microben.core.array.MultiArraySimple")); + } + + @Test + public void testMultiArrayComplex() { + checkAssertions(run("qilin.microben.core.array.MultiArrayComplex")); + } + + @Test + public void testArrayCopy() { + checkAssertions(run("qilin.microben.core.array.ArrayCopy")); + } + + @Test + public void testArrayElemTypeFiltering() { + checkAssertions(run("qilin.microben.core.array.ArrayElemTypeFiltering")); + } +} diff --git a/sootup.qilin/src/test/java/qilin/test/core/AssignTests.java b/sootup.qilin/src/test/java/qilin/test/core/AssignTests.java new file mode 100644 index 00000000000..686c22e90f6 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/test/core/AssignTests.java @@ -0,0 +1,89 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.test.core; + +import org.junit.Test; +import qilin.pta.PTAConfig; +import qilin.test.util.JunitTests; + +public class AssignTests extends JunitTests { + @Test + public void testCastFail() { + checkAssertions(run("qilin.microben.core.assign.CastFail")); + } + + @Test + public void testCastSucc() { + checkAssertions(run("qilin.microben.core.assign.CastSucc")); + } + + @Test + public void testReceiver2This() { + checkAssertions(run("qilin.microben.core.assign.Receiver2This")); + } + + @Test + public void testSimpleAssign() { + checkAssertions(run("qilin.microben.core.assign.SimpleAssign")); + System.out.println(PTAConfig.v().getAppConfig().MAIN_CLASS); + } + + @Test + public void testReturnValue0() { + checkAssertions(run("qilin.microben.core.assign.ReturnValue0")); + System.out.println(PTAConfig.v().getAppConfig().MAIN_CLASS); + } + + @Test + public void testReturnValue1() { + checkAssertions(run("qilin.microben.core.assign.ReturnValue1")); + System.out.println(PTAConfig.v().getAppConfig().MAIN_CLASS); + } + + @Test + public void testReturnValue2() { + checkAssertions(run("qilin.microben.core.assign.ReturnValue2")); + } + + @Test + public void testReturnValue3() { + checkAssertions(run("qilin.microben.core.assign.ReturnValue3")); + } + + @Test + public void testInterAssign() { + checkAssertions(run("qilin.microben.core.assign.InterAssign")); + } + + @Test + public void testStaticParameter() { + checkAssertions(run("qilin.microben.core.assign.StaticParameter")); + } + + @Test + public void testNullPointer() { + checkAssertions(run("qilin.microben.core.assign.NullPointer")); + } + + @Test + public void testRecursion() { + checkAssertions(run("qilin.microben.core.assign.Recursion")); + System.out.println(PTAConfig.v().getAppConfig().MAIN_CLASS); + } +} diff --git a/sootup.qilin/src/test/java/qilin/test/core/CallTests.java b/sootup.qilin/src/test/java/qilin/test/core/CallTests.java new file mode 100644 index 00000000000..9345667ed3a --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/test/core/CallTests.java @@ -0,0 +1,39 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.test.core; + +import org.junit.Test; +import qilin.test.util.JunitTests; + +public class CallTests extends JunitTests { + @Test + public void testVirtualCall0() { + checkAssertions(run("qilin.microben.core.call.VirtualCall0")); + } + + @Test + public void testVirtualCall1() { + checkAssertions(run("qilin.microben.core.call.VirtualCall1")); + } + + @Test + public void testStaticCall() { + checkAssertions(run("qilin.microben.core.call.StaticCall")); + } +} diff --git a/sootup.qilin/src/test/java/qilin/test/core/ClinitTests.java b/sootup.qilin/src/test/java/qilin/test/core/ClinitTests.java new file mode 100644 index 00000000000..e763ced70a7 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/test/core/ClinitTests.java @@ -0,0 +1,44 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.test.core; + +import org.junit.Test; +import qilin.test.util.JunitTests; + +public class ClinitTests extends JunitTests { + @Test + public void testClinitNewExpr() { + checkAssertions(run("qilin.microben.core.clinit.ClinitNewExpr")); + } + + @Test + public void testClinitStaticCall() { + checkAssertions(run("qilin.microben.core.clinit.ClinitStaticCall")); + } + + @Test + public void testClinitStaticLoad() { + checkAssertions(run("qilin.microben.core.clinit.ClinitStaticLoad")); + } + + @Test + public void testClinitStaticStore() { + checkAssertions(run("qilin.microben.core.clinit.ClinitStaticStore")); + } +} diff --git a/sootup.qilin/src/test/java/qilin/test/core/ExceptionTests.java b/sootup.qilin/src/test/java/qilin/test/core/ExceptionTests.java new file mode 100644 index 00000000000..af0c61ff658 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/test/core/ExceptionTests.java @@ -0,0 +1,39 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.test.core; + +import org.junit.Test; +import qilin.test.util.JunitTests; + +public class ExceptionTests extends JunitTests { + @Test + public void testSimpleException() { + checkAssertions(run("qilin.microben.core.exception.SimpleException")); + } + + @Test + public void testExceptionChain() { + checkAssertions(run("qilin.microben.core.exception.ExceptionChain")); + } + + @Test + public void testMethodThrow() { + checkAssertions(run("qilin.microben.core.exception.MethodThrow")); + } +} diff --git a/sootup.qilin/src/test/java/qilin/test/core/FieldTests.java b/sootup.qilin/src/test/java/qilin/test/core/FieldTests.java new file mode 100644 index 00000000000..96d9b628a3b --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/test/core/FieldTests.java @@ -0,0 +1,44 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.test.core; + +import org.junit.Test; +import qilin.test.util.JunitTests; + +public class FieldTests extends JunitTests { + @Test + public void testInstanceLoad() { + checkAssertions(run("qilin.microben.core.field.InstanceLoad")); + } + + @Test + public void testInstanceStore() { + checkAssertions(run("qilin.microben.core.field.InstanceStore")); + } + + @Test + public void testFieldSensitivity1() { + checkAssertions(run("qilin.microben.core.field.FieldSensitivity1")); + } + + @Test + public void testFieldSensitivity2() { + checkAssertions(run("qilin.microben.core.field.FieldSensitivity2")); + } +} diff --git a/sootup.qilin/src/test/java/qilin/test/core/GlobalTests.java b/sootup.qilin/src/test/java/qilin/test/core/GlobalTests.java new file mode 100644 index 00000000000..334a11aa6f8 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/test/core/GlobalTests.java @@ -0,0 +1,39 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.test.core; + +import org.junit.Test; +import qilin.test.util.JunitTests; + +public class GlobalTests extends JunitTests { + @Test + public void testStaticLoad() { + checkAssertions(run("qilin.microben.core.global.StaticLoad")); + } + + @Test + public void testStaticStore() { + checkAssertions(run("qilin.microben.core.global.StaticStore")); + } + + @Test + public void testStringConstant() { + checkAssertions(run("qilin.microben.core.global.StringConstant")); + } +} diff --git a/sootup.qilin/src/test/java/qilin/test/core/NativeTests.java b/sootup.qilin/src/test/java/qilin/test/core/NativeTests.java new file mode 100644 index 00000000000..fd0be1a5742 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/test/core/NativeTests.java @@ -0,0 +1,91 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.test.core; + +import org.junit.Ignore; +import org.junit.Test; +import qilin.test.util.JunitTests; + +public class NativeTests extends JunitTests { + @Test + public void testArrayCopy() { + checkAssertions(run("qilin.microben.core.natives.ArrayCopy")); + } + + @Test + public void testObjectClone() { + checkAssertions(run("qilin.microben.core.natives.ObjectClone")); + } + + @Test + public void testPrivilegedActions0() { + checkAssertions(run("qilin.microben.core.natives.PrivilegedActions0")); + } + + @Test + public void testPrivilegedActions1() { + checkAssertions(run("qilin.microben.core.natives.PrivilegedActions1")); + } + + @Test + public void testPrivilegedActions2() { + checkAssertions(run("qilin.microben.core.natives.PrivilegedActions2", "2o")); + } + + @Test + public void testSystemIn() { + checkAssertions(run("qilin.microben.core.natives.SystemIn")); + } + + @Test + public void testSystemOut() { + checkAssertions(run("qilin.microben.core.natives.SystemOut")); + } + + @Test + public void testSystemErr() { + checkAssertions(run("qilin.microben.core.natives.SystemErr")); + } + + @Test + public void testFinalize() { + checkAssertions(run("qilin.microben.core.natives.Finalize")); + } + + @Test + public void testThreadRun() { + checkAssertions(run("qilin.microben.core.natives.ThreadRun")); + } + + @Test + @Ignore + public void testCurrentThread() { + checkAssertions(run("qilin.microben.core.natives.CurrentThread")); + } + + @Test + public void testRefArrayGet() { + checkAssertions(run("qilin.microben.core.natives.RefArrayGet")); + } + + @Test + public void testRefArraySet() { + checkAssertions(run("qilin.microben.core.natives.RefArraySet")); + } +} diff --git a/sootup.qilin/src/test/java/qilin/test/core/ReflogTests.java b/sootup.qilin/src/test/java/qilin/test/core/ReflogTests.java new file mode 100644 index 00000000000..3f51fea6d03 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/test/core/ReflogTests.java @@ -0,0 +1,88 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.test.core; + +import org.junit.Test; +import qilin.core.PTA; +import qilin.test.util.JunitTests; + +public class ReflogTests extends JunitTests { + @Test + public void testFieldGetStatic() { + checkAssertions(run("qilin.microben.core.reflog.FieldGetStatic")); + } + + @Test + public void testFieldGet() { + checkAssertions(run("qilin.microben.core.reflog.FieldGet")); + } + + @Test + public void testFieldSetStatic() { + checkAssertions(run("qilin.microben.core.reflog.FieldSetStatic")); + } + + @Test + public void testFieldSet() { + checkAssertions(run("qilin.microben.core.reflog.FieldSet")); + } + + @Test + public void testArrayNewInstance() { + checkAssertions(run("qilin.microben.core.reflog.ArrayNewInstance")); + } + + @Test + public void testConstructorNewInstance() { + checkAssertions(run("qilin.microben.core.reflog.ConstructorNewInstance")); + } + + @Test + public void testMethodInvokeStatic() { + checkAssertions(run("qilin.microben.core.reflog.MethodInvokeStatic")); + } + + @Test + public void testMethodInvoke() { + checkAssertions(run("qilin.microben.core.reflog.MethodInvoke")); + } + + @Test + public void testClassNewInstance() { + checkAssertions(run("qilin.microben.core.reflog.ClassNewInstance")); + } + + @Test + public void testDoopRefBug() { + PTA pta = run("qilin.microben.core.reflog.DoopRefBug"); + checkAssertions(pta); + } + + @Test + public void testClassForName() { + PTA pta = run("qilin.microben.core.reflog.ClassForName"); + checkAssertions(pta); + } + + @Test + public void testClassForName1() { + PTA pta = run("qilin.microben.core.reflog.ClassForName1"); + checkAssertions(pta); + } +} diff --git a/sootup.qilin/src/test/java/qilin/test/util/AliasAssertion.java b/sootup.qilin/src/test/java/qilin/test/util/AliasAssertion.java new file mode 100644 index 00000000000..2104c2a119d --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/test/util/AliasAssertion.java @@ -0,0 +1,123 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.test.util; + +import java.util.Objects; +import qilin.core.PTA; +import qilin.core.pag.LocalVarNode; +import qilin.core.sets.PointsToSet; +import qilin.pta.PTAConfig; +import qilin.util.PTAUtils; +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.basic.Value; +import sootup.core.jimple.common.constant.ClassConstant; +import sootup.core.jimple.common.constant.NullConstant; +import sootup.core.jimple.common.constant.StringConstant; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.SootMethod; + +public class AliasAssertion implements IAssertion { + private final PTA pta; + private final SootMethod sm; + private final Stmt stmt; + private final Value va; + private final Value vb; + private final boolean groundTruth; + + public AliasAssertion( + PTA pta, SootMethod sm, Stmt stmt, Value va, Value vb, boolean groundTruth) { + this.pta = pta; + this.sm = sm; + this.stmt = stmt; + this.va = va; + this.vb = vb; + this.groundTruth = groundTruth; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AliasAssertion that = (AliasAssertion) o; + return groundTruth == that.groundTruth + && Objects.equals(sm, that.sm) + && Objects.equals(stmt, that.stmt) + && Objects.equals(va, that.va) + && Objects.equals(vb, that.vb); + } + + @Override + public int hashCode() { + return Objects.hash(sm, stmt, va, vb, groundTruth); + } + + @Override + public boolean check() { + return isMayAlias(pta, va, vb) == groundTruth; + } + + private static boolean DEBUG = true; + + protected boolean isMayAlias(PTA pta, Value va, Value vb) { + if (va instanceof NullConstant && vb instanceof NullConstant) { + return true; + } + if (va instanceof NullConstant || vb instanceof NullConstant) { + return false; + } + if (va instanceof StringConstant && vb instanceof StringConstant) { + return va.equals(vb); + } else if (va instanceof StringConstant) { + StringConstant strConst = (StringConstant) va; + String s = strConst.getValue(); + if (!PTAConfig.v().getPtaConfig().stringConstants) { + s = "STRING_NODE"; + } + PointsToSet pts = pta.reachingObjects(sm, (Local) vb).toCIPointsToSet(); + return pts.possibleStringConstants().contains(s); + } else if (vb instanceof StringConstant) { + StringConstant strConst = (StringConstant) vb; + String s = strConst.getValue(); + if (!PTAConfig.v().getPtaConfig().stringConstants) { + s = "STRING_NODE"; + } + PointsToSet pts = pta.reachingObjects(sm, (Local) va).toCIPointsToSet(); + return pts.possibleStringConstants().contains(s); + } else if (va instanceof ClassConstant) { + PointsToSet pts = pta.reachingObjects(sm, (Local) vb).toCIPointsToSet(); + return pts.possibleClassConstants().contains(va); + } else if (vb instanceof ClassConstant) { + PointsToSet pts = pta.reachingObjects(sm, (Local) va).toCIPointsToSet(); + return pts.possibleClassConstants().contains(vb); + } + PointsToSet pts1 = pta.reachingObjects(sm, (Local) va).toCIPointsToSet(); + if (DEBUG) { + LocalVarNode lvn = pta.getPag().findLocalVarNode(sm, va, va.getType()); + System.out.println("va points to: " + PTAUtils.getNodeLabel(lvn) + lvn); + PTAUtils.printPts(pta, pts1); + } + PointsToSet pts2 = pta.reachingObjects(sm, (Local) vb).toCIPointsToSet(); + if (DEBUG) { + LocalVarNode lvn = pta.getPag().findLocalVarNode(sm, vb, vb.getType()); + System.out.println("vb points to: " + PTAUtils.getNodeLabel(lvn) + lvn); + PTAUtils.printPts(pta, pts2); + } + return pts1.hasNonEmptyIntersection(pts2); + } +} diff --git a/sootup.qilin/src/test/java/qilin/test/util/AssertionsParser.java b/sootup.qilin/src/test/java/qilin/test/util/AssertionsParser.java new file mode 100644 index 00000000000..1bddbff2f90 --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/test/util/AssertionsParser.java @@ -0,0 +1,71 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.test.util; + +import java.util.HashSet; +import java.util.Set; +import qilin.core.PTA; +import qilin.util.PTAUtils; +import sootup.core.jimple.common.expr.AbstractInvokeExpr; +import sootup.core.jimple.common.expr.JStaticInvokeExpr; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.SootMethod; +import sootup.core.signatures.MethodSignature; + +public class AssertionsParser { + protected static String mayAliasSig = + ""; + protected static String notAliasSig = + ""; + + public static Set retrieveQueryInfo(PTA pta) { + Set aliasAssertionSet = new HashSet<>(); + for (SootMethod sm : pta.getNakedReachableMethods()) { + if (sm.isNative()) { + continue; + } + + // if (sm.getSignature().equals("")) { + // System.out.println(sm); + // System.out.println(PTAUtils.getMethodBody(sm)); + // PTAUtils.dumpMPAG(pta, sm); + // + // System.out.println("================================================================="); + // } + for (final Stmt stmt : PTAUtils.getMethodBody(sm).getStmts()) { + if (stmt.containsInvokeExpr()) { + AbstractInvokeExpr ie = stmt.getInvokeExpr(); + if (ie instanceof JStaticInvokeExpr) { + final MethodSignature calleeSig = ie.getMethodSignature(); + if (calleeSig.toString().equals(mayAliasSig)) { + aliasAssertionSet.add( + new AliasAssertion(pta, sm, stmt, ie.getArg(0), ie.getArg(1), true)); + } else if (calleeSig.toString().equals(notAliasSig)) { + aliasAssertionSet.add( + new AliasAssertion(pta, sm, stmt, ie.getArg(0), ie.getArg(1), false)); + } + } + } + } + } + System.out.println("#alias queries: " + aliasAssertionSet.size()); + return aliasAssertionSet; + } +} diff --git a/sootup.qilin/src/test/java/qilin/test/util/IAssertion.java b/sootup.qilin/src/test/java/qilin/test/util/IAssertion.java new file mode 100644 index 00000000000..b4ef9f4e33b --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/test/util/IAssertion.java @@ -0,0 +1,23 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.test.util; + +public interface IAssertion { + boolean check(); +} diff --git a/sootup.qilin/src/test/java/qilin/test/util/JunitTests.java b/sootup.qilin/src/test/java/qilin/test/util/JunitTests.java new file mode 100644 index 00000000000..e1df9d52a9e --- /dev/null +++ b/sootup.qilin/src/test/java/qilin/test/util/JunitTests.java @@ -0,0 +1,131 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.test.util; + +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.IOException; +import java.util.Set; +import org.junit.BeforeClass; +import qilin.core.PTA; +import qilin.driver.PTAFactory; +import qilin.driver.PTAOption; +import qilin.driver.PTAPattern; +import qilin.pta.PTAConfig; +import qilin.util.PTAUtils; +import sootup.core.views.View; + +public abstract class JunitTests { + protected static String appPath, jrePath, refLogPath; + protected static boolean isSetUp = false; + + @BeforeClass + public static void setUp() throws IOException { + if (isSetUp) { + return; + } + File rootDir = new File("../"); + File testDir = + new File( + rootDir, "sootup.qilin" + File.separator + "target" + File.separator + "test-classes"); + appPath = testDir.getCanonicalPath(); + System.out.println("APP_PATH:" + appPath); + File refLogDir = + new File( + rootDir, + "sootup.qilin" + + File.separator + + "src" + + File.separator + + "test" + + File.separator + + "java" + + File.separator + + "qilin" + + File.separator + + "microben" + + File.separator + + "core" + + File.separator + + "reflog"); + refLogPath = refLogDir.getCanonicalPath(); + File jreFile = + new File( + ".." + + File.separator + + "artifact" + + File.separator + + "benchmarks" + + File.separator + + "JREs" + + File.separator + + "jre1.6.0_45" + // + "jre1.8.0_121_debug" + ); + jrePath = jreFile.getCanonicalPath(); + String[] args = generateArgumentsx(); + PTAOption ptaOption = new PTAOption(); + ptaOption.parseCommandLine(args); + isSetUp = true; + } + + public PTA run(String mainClass) { + return run(mainClass, "insens"); + } + + public PTA run(String mainClass, String ptaPattern) { + PTAConfig.v().getAppConfig().MAIN_CLASS = mainClass; + PTAConfig.v().getPtaConfig().ptaPattern = new PTAPattern(ptaPattern); + PTAConfig.v().getPtaConfig().ptaName = PTAConfig.v().getPtaConfig().ptaPattern.toString(); + System.out.println(PTAConfig.v().getAppConfig().APP_PATH); + View view = PTAUtils.createView(); + PTA pta = PTAFactory.createPTA(PTAConfig.v().getPtaConfig().ptaPattern, view, mainClass); + pta.pureRun(); + return pta; + } + + public static String[] generateArgumentsx() { + return new String[] { + "-singleentry", + "-apppath", + appPath, + "-mainclass", + "qilin.microben.core.exception.SimpleException", + "-se", + "-jre=" + jrePath, + "-clinit=ONFLY", + "-lcs", + "-mh", + "-pae", + "-pe", + "-reflectionlog", + refLogPath + File.separator + "Reflection.log" + }; + } + + protected void checkAssertions(PTA pta) { + Set aliasAssertionSet = AssertionsParser.retrieveQueryInfo(pta); + for (IAssertion mAssert : aliasAssertionSet) { + boolean answer = mAssert.check(); + System.out.println("Assertion is " + answer); + assertTrue(answer); + } + } +} diff --git a/sootup.report/pom.xml b/sootup.report/pom.xml index c878def6996..1068a2f7fb4 100644 --- a/sootup.report/pom.xml +++ b/sootup.report/pom.xml @@ -8,7 +8,7 @@ org.soot-oss sootup - 1.2.1-SNAPSHOT + 1.3.1-SNAPSHOT diff --git a/sootup.tests/pom.xml b/sootup.tests/pom.xml index 4e6e88ce2f7..3289614075b 100644 --- a/sootup.tests/pom.xml +++ b/sootup.tests/pom.xml @@ -10,7 +10,7 @@ org.soot-oss sootup - 1.2.1-SNAPSHOT + 1.3.1-SNAPSHOT diff --git a/sootup.tests/src/test/java/sootup/tests/validator/MethodValidatorTest.java b/sootup.tests/src/test/java/sootup/tests/validator/MethodValidatorTest.java index 5b867b67213..7a4c9461999 100644 --- a/sootup.tests/src/test/java/sootup/tests/validator/MethodValidatorTest.java +++ b/sootup.tests/src/test/java/sootup/tests/validator/MethodValidatorTest.java @@ -5,6 +5,7 @@ import java.nio.file.Paths; import java.util.*; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import sootup.core.model.Body; import sootup.core.model.SootClass; @@ -17,6 +18,7 @@ import sootup.jimple.parser.JimpleAnalysisInputLocation; import sootup.jimple.parser.JimpleView; +@Tag("Java8") public class MethodValidatorTest { MethodValidator methodValidator = new MethodValidator(); static JimpleView jimpleView; diff --git a/sootup.tests/src/test/java/sootup/tests/validator/TypesValidatorTest.java b/sootup.tests/src/test/java/sootup/tests/validator/TypesValidatorTest.java index 4a75c2f9b0d..b7dd6f69ed5 100644 --- a/sootup.tests/src/test/java/sootup/tests/validator/TypesValidatorTest.java +++ b/sootup.tests/src/test/java/sootup/tests/validator/TypesValidatorTest.java @@ -6,6 +6,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import sootup.core.frontend.OverridingBodySource; import sootup.core.inputlocation.EagerInputLocation; @@ -25,6 +26,7 @@ import sootup.java.core.views.JavaView; import sootup.java.core.views.MutableJavaView; +@Tag("Java8") public class TypesValidatorTest { static MutableJavaView view; static TypesValidator typesValidator; diff --git a/sootup.tests/src/test/resources/methoddispatchresolver/AllDispatch/A.java b/sootup.tests/src/test/resources/methoddispatchresolver/AllDispatch/A.java new file mode 100644 index 00000000000..ee9d7735d8e --- /dev/null +++ b/sootup.tests/src/test/resources/methoddispatchresolver/AllDispatch/A.java @@ -0,0 +1,7 @@ + +/** @author: Jonas Klauke **/ + +public class A { + public void method(){ + } +} \ No newline at end of file diff --git a/sootup.tests/src/test/resources/methoddispatchresolver/AllDispatch/AbstractClass.java b/sootup.tests/src/test/resources/methoddispatchresolver/AllDispatch/AbstractClass.java new file mode 100644 index 00000000000..3de86bb38d1 --- /dev/null +++ b/sootup.tests/src/test/resources/methoddispatchresolver/AllDispatch/AbstractClass.java @@ -0,0 +1,6 @@ + +/** @author: Jonas Klauke **/ + +public abstract class AbstractClass extends A{ + public abstract void method(); +} \ No newline at end of file diff --git a/sootup.tests/src/test/resources/methoddispatchresolver/AllDispatch/B.java b/sootup.tests/src/test/resources/methoddispatchresolver/AllDispatch/B.java new file mode 100644 index 00000000000..30144f71c34 --- /dev/null +++ b/sootup.tests/src/test/resources/methoddispatchresolver/AllDispatch/B.java @@ -0,0 +1,5 @@ + +/** @author: Jonas Klauke **/ + +public class B extends AbstractClass { +} \ No newline at end of file diff --git a/sootup.tests/src/test/resources/methoddispatchresolver/AllDispatch/C.java b/sootup.tests/src/test/resources/methoddispatchresolver/AllDispatch/C.java new file mode 100644 index 00000000000..f482c4f0787 --- /dev/null +++ b/sootup.tests/src/test/resources/methoddispatchresolver/AllDispatch/C.java @@ -0,0 +1,8 @@ + +/** @author: Jonas Klauke **/ + +public class C extends A { + public void method(){ + int num = 20; + } +} \ No newline at end of file diff --git a/sootup.tests/src/test/resources/methoddispatchresolver/CovarianceDispatch/Subclass.java b/sootup.tests/src/test/resources/methoddispatchresolver/CovarianceDispatch/Subclass.java new file mode 100644 index 00000000000..138c1993d73 --- /dev/null +++ b/sootup.tests/src/test/resources/methoddispatchresolver/CovarianceDispatch/Subclass.java @@ -0,0 +1,8 @@ + +/** @author: Jonas Klauke **/ + +public class Subclass extends Superclass { + public Superclass method(){ + return new Superclass(); + } +} \ No newline at end of file diff --git a/sootup.tests/src/test/resources/methoddispatchresolver/CovarianceDispatch/Superclass.java b/sootup.tests/src/test/resources/methoddispatchresolver/CovarianceDispatch/Superclass.java new file mode 100644 index 00000000000..f041d65e412 --- /dev/null +++ b/sootup.tests/src/test/resources/methoddispatchresolver/CovarianceDispatch/Superclass.java @@ -0,0 +1,8 @@ + +/** @author: Jonas Klauke **/ + +public class Superclass{ + public Object method(){ + return new Object(); + } +} \ No newline at end of file