diff --git a/src/main/java/de/fraunhofer/aisec/cpg/TranslationConfiguration.java b/src/main/java/de/fraunhofer/aisec/cpg/TranslationConfiguration.java index 72c4694534..08aa5bca79 100644 --- a/src/main/java/de/fraunhofer/aisec/cpg/TranslationConfiguration.java +++ b/src/main/java/de/fraunhofer/aisec/cpg/TranslationConfiguration.java @@ -327,6 +327,7 @@ public Builder defaultPasses() { registerPass(new CallResolver()); // creates CG registerPass(new EvaluationOrderGraphPass()); // creates EOG registerPass(new TypeResolver()); + registerPass(new ControlFlowSensitiveDFGPass()); return this; } diff --git a/src/main/java/de/fraunhofer/aisec/cpg/graph/DeclaredReferenceExpression.java b/src/main/java/de/fraunhofer/aisec/cpg/graph/DeclaredReferenceExpression.java index d3b102b09c..8b28fc5acb 100644 --- a/src/main/java/de/fraunhofer/aisec/cpg/graph/DeclaredReferenceExpression.java +++ b/src/main/java/de/fraunhofer/aisec/cpg/graph/DeclaredReferenceExpression.java @@ -55,6 +55,10 @@ public Set getRefersTo() { return refersTo; } + public AccessValues getAccess() { + return access; + } + public void setRefersTo(@NonNull ValueDeclaration refersTo) { HashSet n = new HashSet<>(); n.add(refersTo); diff --git a/src/main/java/de/fraunhofer/aisec/cpg/helpers/ControlFlowSensitiveDFG.java b/src/main/java/de/fraunhofer/aisec/cpg/helpers/ControlFlowSensitiveDFG.java new file mode 100644 index 0000000000..e8d91ef1fa --- /dev/null +++ b/src/main/java/de/fraunhofer/aisec/cpg/helpers/ControlFlowSensitiveDFG.java @@ -0,0 +1,336 @@ +package de.fraunhofer.aisec.cpg.helpers; + +import de.fraunhofer.aisec.cpg.graph.*; +import java.util.*; + +public class ControlFlowSensitiveDFG { + /* + Map tracking every VariableDeclaration and all the possible Values. For the ControlFlowSensitiveDFG + only VariableDeclarations are tracked, as we cannot determine the values for FieldDeclarations, since + it depends on the external execution order. + */ + private Map> variables; + private Set visited = new HashSet<>(); + private Set visitedEOG; + + // A Node with refined DFG edges (key) is mapped to a set of nodes that were the previous + // (unrefined) DFG edges and need to be removed later on + private Map> removes; + // Node where the ControlFlowSensitive analysis is started. On analysis start this will be the + // MethodDeclaration, but + // it can be any other node where the analysis is splitted (such as if of switch) + private Node startNode; + + // Node where a splitted analysis is joined back together. If there is no split this is null. + private Node endNode; + + public ControlFlowSensitiveDFG( + Node startNode, + Node endNode, + Map> variables, + Set visitedEOG) { + this.variables = duplicateMap(variables); + this.startNode = startNode; + this.endNode = endNode; + this.visitedEOG = new HashSet<>(visitedEOG); + this.removes = new HashMap<>(); + } + + public ControlFlowSensitiveDFG(Node startNode) { + this.variables = new HashMap<>(); + this.startNode = startNode; + this.endNode = null; + this.visitedEOG = new HashSet<>(); + this.removes = new HashMap<>(); + } + + public Map> getRemoves() { + return removes; + } + + private void addToRemoves(Node curr, Node prev) { + if (!this.removes.containsKey(curr)) { + this.removes.put(curr, new HashSet<>()); + } + + this.removes.get(curr).add(prev); + } + + public Map> getVariables() { + return variables; + } + + public Set getVisitedEOG() { + return visitedEOG; + } + + private boolean checkVisited(Node node) { + return this.visited.contains(node); + } + + private static Map> duplicateMap( + Map> in) { + Map> duplicatedVariables = new HashMap<>(); + + for (Map.Entry> e : in.entrySet()) { + Set nodes = new HashSet<>(e.getValue()); + duplicatedVariables.put(e.getKey(), nodes); + } + + return duplicatedVariables; + } + + private void addVisitedToMap(VariableDeclaration variableDeclaration) { + Set prevDFGSet = variableDeclaration.getPrevDFG(); + for (Node prev : prevDFGSet) { + if (checkVisited(prev)) { + if (variables.containsKey(variableDeclaration)) { + variables.get(variableDeclaration).add(prev); + } else { + Set dfgSet = new HashSet<>(); + dfgSet.add(prev); + variables.put(variableDeclaration, dfgSet); + } + } + } + } + + /** + * Reverses the removal of prevDFG for VariableDeclarations perfomed by {@link + * #addVisitedToMap(VariableDeclaration)}, when there a unique DFG path + */ + private void addUniqueDFGs() { + for (Map.Entry> entry : this.variables.entrySet()) { + if (entry.getValue().size() == 1) { + entry.getKey().addPrevDFG(entry.getValue().iterator().next()); + } + } + } + + /** + * Traverses the EOG starting at a node until there is no more outgoing EOGs to new nodes. + * + * @param node starting node + * @return set containing all nodes that have been reached + */ + private Set eogTraversal(Node node) { + Set eogReachableNodes = new HashSet<>(); + Set checkRechable = new HashSet<>(); + checkRechable.add(node); + + while (!checkRechable.isEmpty()) { + Node n = checkRechable.iterator().next(); + checkRechable.addAll(n.getNextEOG()); + eogReachableNodes.add(n); + checkRechable.removeAll(eogReachableNodes); + } + + return eogReachableNodes; + } + + /** + * @param node that has multiple outgoing EOG edges (e.g. IfStatement or SwitchStatement. Not + * applicable to TryCatch, since control flow is not fully mutually exclusive + * @return the first node at which the execution of both paths are joined + */ + private Node obtainJoinPoint(Node node) { + Set nextEOG = new HashSet<>(node.getNextEOG()); + List> eogs = new ArrayList<>(); + for (Node next : nextEOG) { + eogs.add(eogTraversal(next)); + } + + // Calculate intersection to locate point in which the execution paths join + Set intersection = new HashSet<>(eogs.get(0)); + + for (Set eog : eogs) { + intersection.retainAll(eog); + } + + // Find first Element of EOG Traversal: + Node element = null; + while (intersection.size() > 1) { + if (element != null) { + intersection.remove(element); + } + Optional elementFromSet = intersection.stream().findFirst(); + if (elementFromSet.isPresent()) { + element = elementFromSet.get(); + Set eog = eogTraversal(element); + eog.remove(element); + intersection.removeAll(eog); + } + } + + Optional joinNode = intersection.stream().findAny(); + return joinNode.orElse(null); + } + + /** + * Merges the variable Map when the analysis has been split and is joined together. + * + * @param dfgs List of all ControlFlowSensitiveDFGs that are necessary depending on the number of + * cases (e.g. if-else contains two dfgs, one for if and one for the else block. + * @return merged Map with the VariableDeclaration to Value mappings of all the dfgs. Due to the + * merge it is possible to have multiple values for one VariableDeclaration (e.g. when a + * variable is set to some value x in the if block, and to a value y in the else block). + */ + private Map> joinVariables(List dfgs) { + Map> joindVariables = new HashMap<>(); + + for (ControlFlowSensitiveDFG dfg : dfgs) { + for (VariableDeclaration variableDeclaration : dfg.getVariables().keySet()) { + if (!joindVariables.containsKey(variableDeclaration)) { + Set values = new HashSet<>(dfg.getVariables().get(variableDeclaration)); + joindVariables.put(variableDeclaration, values); + } else { + joindVariables + .get(variableDeclaration) + .addAll(dfg.getVariables().get(variableDeclaration)); + } + } + } + + return joindVariables; + } + + /** + * @param dfgs exclusive DFG Paths + * @return combination of all dfg paths that need to be removed for every exclusive DFG Path + */ + private Map> joinRemoves(List dfgs) { + Map> newRemoves = new HashMap<>(); + for (ControlFlowSensitiveDFG dfg : dfgs) { + for (Node n : dfg.getRemoves().keySet()) { + if (!newRemoves.containsKey(n)) { + newRemoves.put(n, new HashSet<>()); + } + newRemoves.get(n).addAll(dfg.getRemoves().get(n)); + } + } + return newRemoves; + } + + /** + * Stores the prevDFG to a VariableDeclaration of a node in the removes map and adds the values of + * the VariableDeclaration as prevDFGs to the node + * + * @param currNode node that is analyzed + */ + private void setIngoingDFG(Node currNode) { + Set prevDFGs = new HashSet<>(currNode.getPrevDFG()); + for (Node prev : prevDFGs) { + if (prev instanceof VariableDeclaration && variables.containsKey(prev)) { + for (Node target : variables.get(prev)) { + currNode.addPrevDFG(target); + } + addToRemoves(currNode, prev); + } + } + } + + /** + * If a Node has a DFG to a VariableDeclaration we need to remove the nextDFG and store the value + * of the node in our tracking map + * + * @param currNode Node that is being analyzed + */ + private void registerOutgoingDFG(Node currNode) { + Set nextDFG = new HashSet<>(currNode.getNextDFG()); + for (Node next : nextDFG) { + if (next instanceof VariableDeclaration && variables.containsKey(next)) { + Set values = new HashSet<>(currNode.getPrevDFG()); + variables.replace((VariableDeclaration) next, values); + } + } + } + + /** + * Checks which is the next node that should be analyzed by EOG order and checking which have + * already been analyzed + * + * @param currNode current node + * @return node that has currNode as prevDFG and has not yet been analyzed or null if we reached + * the end + */ + private Node getNextEOG(Node currNode) { + for (Node next : currNode.getNextEOG()) { + if (!visitedEOG.contains(next)) { + return next; + } + } + + return null; + } + + /** + * Handles when there is a mutually exclusive split for the DFG + * + * @param currNode node where the split occcurs (e.g. IfStatement or SwitchStatement) + * @return node at which the split is over and both execution paths are equal again + */ + private Node handleDFGSplit(Node currNode) { + // If an IfStatement or a SwitchStatement is found we split the ControlFlowSensitiveDFG for + // every case and merge it, when the execution reaches the joinPoint + Node joinNode = obtainJoinPoint(currNode); + + List eogList = currNode.getNextEOG(); + List dfgs = new ArrayList<>(); + for (Node n : eogList) { + ControlFlowSensitiveDFG dfg = + new ControlFlowSensitiveDFG(n, joinNode, variables, this.visitedEOG); + dfgs.add(dfg); + dfg.handle(); + } + + this.variables = joinVariables(dfgs); + this.removes = joinRemoves(dfgs); + + for (ControlFlowSensitiveDFG dfg : dfgs) { + this.visitedEOG.addAll(dfg.getVisitedEOG()); + } + return joinNode; + } + + /** Main method that performs the ControlFlowSensitveDFG analysis and transformation. */ + public void handle() { + Node currNode = startNode; + while (!visitedEOG.contains(currNode) && currNode != null && !currNode.equals(endNode)) { + Node nextNode = null; + visited.add(currNode); + visitedEOG.add(currNode); + + List nextEOG = currNode.getNextEOG(); + if (nextEOG.isEmpty()) { + break; + } + + if (currNode instanceof VariableDeclaration) { + // New VariableDeclaration is found, and we start the tracking + addVisitedToMap((VariableDeclaration) currNode); + } else if (currNode instanceof IfStatement || currNode instanceof SwitchStatement) { + nextNode = handleDFGSplit(currNode); + + } else if (currNode instanceof DeclaredReferenceExpression) { + // A DeclaredReferenceExpression makes use of one of the VariableDeclaration we are + // tracking. Therefore we must modify the outgoing and ingoing DFG edges + // Check for outgoing DFG edges + registerOutgoingDFG(currNode); + + // Check for ingoing DFG edges + setIngoingDFG(currNode); + } + + // If the nextNode has not been set by a JoinPoint we take the nextEOG + if (nextNode == null) { + nextNode = getNextEOG(currNode); + } + + currNode = nextNode; + } + + if (this.startNode instanceof FunctionDeclaration) { + addUniqueDFGs(); + } + } +} diff --git a/src/main/java/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPass.java b/src/main/java/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPass.java new file mode 100644 index 0000000000..77355ae500 --- /dev/null +++ b/src/main/java/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPass.java @@ -0,0 +1,61 @@ +package de.fraunhofer.aisec.cpg.passes; + +import de.fraunhofer.aisec.cpg.TranslationResult; +import de.fraunhofer.aisec.cpg.graph.FunctionDeclaration; +import de.fraunhofer.aisec.cpg.graph.Node; +import de.fraunhofer.aisec.cpg.graph.TranslationUnitDeclaration; +import de.fraunhofer.aisec.cpg.helpers.ControlFlowSensitiveDFG; +import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker; + +/** + * This pass tracks VariableDeclarations and values that are included in the graph by the DFG edge. + * For this pass we traverse the EOG in order to detect mutually exclusive code blocks (e.g. If-Else + * Statement) in order to remove the DFG edges that are not feasible. + * + *

Control Flow Sensitivity in the DFG is only performed on VariableDeclarations and not on + * FieldDeclarations. The reason for this being the fact, that the value of a field might be + * modified to a value that is not present in the method, thus it is not detected by our variable + * tracking + */ +public class ControlFlowSensitiveDFGPass extends Pass { + + @Override + public void cleanup() { + // Nothing to cleanup + } + + @Override + public void accept(TranslationResult translationResult) { + SubgraphWalker.IterativeGraphWalker walker = new SubgraphWalker.IterativeGraphWalker(); + walker.registerOnNodeVisit(this::handle); + for (TranslationUnitDeclaration tu : translationResult.getTranslationUnits()) { + walker.iterate(tu); + } + } + + /** + * Removes unrefined DFG edges + * + * @param dfg ControlFlowSensitiveDFG of entire Method + */ + private void removeValues(ControlFlowSensitiveDFG dfg) { + for (Node currNode : dfg.getRemoves().keySet()) { + for (Node prev : dfg.getRemoves().get(currNode)) { + currNode.removePrevDFG(prev); + } + } + } + + /** + * ControlFlowSensitiveDFG Pass is perfomed on every Method + * + * @param node every node in the TranslationResult + */ + public void handle(Node node) { + if (node instanceof FunctionDeclaration) { + ControlFlowSensitiveDFG controlFlowSensitiveDFG = new ControlFlowSensitiveDFG(node); + controlFlowSensitiveDFG.handle(); + removeValues(controlFlowSensitiveDFG); + } + } +} diff --git a/src/test/java/de/fraunhofer/aisec/cpg/enhancements/DFGTest.java b/src/test/java/de/fraunhofer/aisec/cpg/enhancements/DFGTest.java new file mode 100644 index 0000000000..27f9e956bc --- /dev/null +++ b/src/test/java/de/fraunhofer/aisec/cpg/enhancements/DFGTest.java @@ -0,0 +1,235 @@ +package de.fraunhofer.aisec.cpg.enhancements; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import de.fraunhofer.aisec.cpg.TestUtils; +import de.fraunhofer.aisec.cpg.graph.*; +import java.nio.file.Path; +import java.util.List; +import java.util.Set; +import org.junit.jupiter.api.Test; + +class DFGTest { + // Test DFG + + // Test ControlFlowSensitiveDFGPass + @Test + void testControlSensitiveDFGPassIfMerge() throws Exception { + Path topLevel = Path.of("src", "test", "resources", "dfg"); + List result = + TestUtils.analyze( + List.of(topLevel.resolve("ControlFlowSensitiveDFGIfMerge.java").toFile()), + topLevel, + true); + + // Test If-Block + Literal literal2 = + TestUtils.findByPredicate( + TestUtils.subnodesOfType(result, Literal.class), l -> l.getValue().equals(2)); + + DeclaredReferenceExpression a2 = + TestUtils.findByPredicate( + TestUtils.subnodesOfType(result, DeclaredReferenceExpression.class), + e -> e.getAccess().equals(AccessValues.WRITE)); + + assertTrue(literal2.getNextDFG().contains(a2)); + assertEquals(1, a2.getNextDFG().size()); // Outgoing DFG Edges only to VariableDeclaration + + assertEquals(1, a2.getRefersTo().size()); + Node refersTo = a2.getRefersTo().iterator().next(); + assertTrue(refersTo instanceof VariableDeclaration); + VariableDeclaration a = (VariableDeclaration) refersTo; + assertEquals(0, a.getNextDFG().size()); + assertEquals(a2.getNextDFG().iterator().next(), a); + + // Test Else-Block with System.out.println() + Literal literal1 = + TestUtils.findByPredicate( + TestUtils.subnodesOfType(result, Literal.class), l -> l.getValue().equals(1)); + + CallExpression println = + TestUtils.findByPredicate( + TestUtils.subnodesOfType(result, CallExpression.class), + c -> c.getName().equals("println")); + + DeclaredReferenceExpression a1 = + TestUtils.findByPredicate( + TestUtils.subnodesOfType(result, DeclaredReferenceExpression.class), + e -> e.getNextEOG().contains(println)); + + assertEquals(1, a1.getPrevDFG().size()); + assertEquals(literal1, a1.getPrevDFG().iterator().next()); + + assertEquals(1, a1.getNextEOG().size()); + assertEquals(println, a1.getNextEOG().get(0)); + + // Test Merging + VariableDeclaration b = + TestUtils.findByPredicate( + TestUtils.subnodesOfType(result, VariableDeclaration.class), + v -> v.getName().equals("b")); + + DeclaredReferenceExpression ab = (DeclaredReferenceExpression) b.getPrevEOG().get(0); + + assertTrue(literal1.getNextDFG().contains(ab)); + assertTrue(literal2.getNextDFG().contains(ab)); + } + + /** + * Tests the ControlFlowSensitiveDFGPass and checks if an assignment located within one block + * clears the values from the map and includes only the new (assigned) value. + * + * @throws Exception Any exception that happens during the analysis process + */ + @Test + void testControlSensitiveDFGPassIfNoMerge() throws Exception { + Path topLevel = Path.of("src", "test", "resources", "dfg"); + List result = + TestUtils.analyze( + List.of(topLevel.resolve("ControlFlowSensitiveDFGIfNoMerge.java").toFile()), + topLevel, + true); + + VariableDeclaration b = + TestUtils.findByPredicate( + TestUtils.subnodesOfType(result, VariableDeclaration.class), + v -> v.getName().equals("b")); + + DeclaredReferenceExpression ab = (DeclaredReferenceExpression) b.getPrevEOG().get(0); + + Literal literal4 = + TestUtils.findByPredicate( + TestUtils.subnodesOfType(result, Literal.class), l -> l.getValue().equals(4)); + + assertTrue(literal4.getNextDFG().contains(ab)); + assertEquals(1, ab.getPrevDFG().size()); + } + + @Test + void testControlSensitiveDFGPassSwitch() throws Exception { + Path topLevel = Path.of("src", "test", "resources", "dfg"); + List result = + TestUtils.analyze( + List.of(topLevel.resolve("ControlFlowSensitiveDFGSwitch.java").toFile()), + topLevel, + true); + + VariableDeclaration a = + TestUtils.findByPredicate( + TestUtils.subnodesOfType(result, VariableDeclaration.class), + v -> v.getName().equals("a")); + + VariableDeclaration b = + TestUtils.findByPredicate( + TestUtils.subnodesOfType(result, VariableDeclaration.class), + v -> v.getName().equals("b")); + + DeclaredReferenceExpression ab = (DeclaredReferenceExpression) b.getPrevEOG().get(0); + + DeclaredReferenceExpression a10 = + TestUtils.findByPredicate( + TestUtils.subnodesOfType(result, DeclaredReferenceExpression.class), + dre -> dre.getLocation().getRegion().getStartLine() == 8); + DeclaredReferenceExpression a11 = + TestUtils.findByPredicate( + TestUtils.subnodesOfType(result, DeclaredReferenceExpression.class), + dre -> dre.getLocation().getRegion().getStartLine() == 11); + DeclaredReferenceExpression a12 = + TestUtils.findByPredicate( + TestUtils.subnodesOfType(result, DeclaredReferenceExpression.class), + dre -> dre.getLocation().getRegion().getStartLine() == 14); + + Literal literal0 = + TestUtils.findByPredicate( + TestUtils.subnodesOfType(result, Literal.class), l -> l.getValue().equals(0)); + Literal literal10 = + TestUtils.findByPredicate( + TestUtils.subnodesOfType(result, Literal.class), l -> l.getValue().equals(10)); + Literal literal11 = + TestUtils.findByPredicate( + TestUtils.subnodesOfType(result, Literal.class), l -> l.getValue().equals(11)); + Literal literal12 = + TestUtils.findByPredicate( + TestUtils.subnodesOfType(result, Literal.class), l -> l.getValue().equals(12)); + + assertEquals(2, literal10.getNextDFG().size()); + assertTrue(literal10.getNextDFG().contains(a10)); + + assertEquals(2, literal11.getNextDFG().size()); + assertTrue(literal11.getNextDFG().contains(a11)); + + assertEquals(3, literal12.getNextDFG().size()); + assertTrue(literal12.getNextDFG().contains(a12)); + + assertEquals(4, a.getPrevDFG().size()); + assertTrue(a.getPrevDFG().contains(literal0)); + assertTrue(a.getPrevDFG().contains(a10)); + assertTrue(a.getPrevDFG().contains(a11)); + assertTrue(a.getPrevDFG().contains(a12)); + + assertTrue(ab.getPrevDFG().contains(literal0)); + assertTrue(ab.getPrevDFG().contains(literal10)); + assertTrue(ab.getPrevDFG().contains(literal11)); + assertTrue(ab.getPrevDFG().contains(literal12)); + + assertEquals(1, ab.getNextDFG().size()); + assertTrue(ab.getNextDFG().contains(b)); + + // Fallthrough test + CallExpression println = + TestUtils.findByPredicate( + TestUtils.subnodesOfType(result, CallExpression.class), + c -> c.getName().equals("println")); + + DeclaredReferenceExpression aPrintln = + TestUtils.findByPredicate( + TestUtils.subnodesOfType(result, DeclaredReferenceExpression.class), + e -> e.getNextEOG().contains(println)); + + assertEquals(2, aPrintln.getPrevDFG().size()); + assertTrue(aPrintln.getPrevDFG().contains(literal0)); + assertTrue(aPrintln.getPrevDFG().contains(literal12)); + } + + // Test DFG when ReadWrite access occurs, such as compoundoperators or unaryoperators + + @Test + void testCompoundOperatorDFG() throws Exception { + Path topLevel = Path.of("src", "test", "resources", "dfg"); + List result = + TestUtils.analyze( + List.of(topLevel.resolve("compoundoperator.cpp").toFile()), topLevel, true); + + BinaryOperator rwCompoundOperator = + TestUtils.findByUniqueName(TestUtils.subnodesOfType(result, BinaryOperator.class), "+="); + DeclaredReferenceExpression expression = + TestUtils.findByUniqueName( + TestUtils.subnodesOfType(result, DeclaredReferenceExpression.class), "i"); + + Set prevDFGOperator = rwCompoundOperator.getPrevDFG(); + Set nextDFGOperator = rwCompoundOperator.getNextDFG(); + + assertTrue(prevDFGOperator.contains(expression)); + assertTrue(nextDFGOperator.contains(expression)); + } + + @Test + void testUnaryOperatorDFG() throws Exception { + Path topLevel = Path.of("src", "test", "resources", "dfg"); + List result = + TestUtils.analyze(List.of(topLevel.resolve("unaryoperator.cpp").toFile()), topLevel, true); + + UnaryOperator rwUnaryOperator = + TestUtils.findByUniqueName(TestUtils.subnodesOfType(result, UnaryOperator.class), "++"); + DeclaredReferenceExpression expression = + TestUtils.findByUniqueName( + TestUtils.subnodesOfType(result, DeclaredReferenceExpression.class), "i"); + + Set prevDFGOperator = rwUnaryOperator.getPrevDFG(); + Set nextDFGOperator = rwUnaryOperator.getNextDFG(); + + assertTrue(prevDFGOperator.contains(expression)); + assertTrue(nextDFGOperator.contains(expression)); + } +} diff --git a/src/test/java/de/fraunhofer/aisec/cpg/enhancements/ReadWriteDFGTest.java b/src/test/java/de/fraunhofer/aisec/cpg/enhancements/ReadWriteDFGTest.java deleted file mode 100644 index a50953f5f8..0000000000 --- a/src/test/java/de/fraunhofer/aisec/cpg/enhancements/ReadWriteDFGTest.java +++ /dev/null @@ -1,70 +0,0 @@ -package de.fraunhofer.aisec.cpg.enhancements; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import de.fraunhofer.aisec.cpg.TestUtils; -import de.fraunhofer.aisec.cpg.graph.*; -import java.nio.file.Path; -import java.util.List; -import java.util.Set; -import org.junit.jupiter.api.Test; - -class ReadWriteDFGTest { - - @Test - void testCompoundOperatorDFG() throws Exception { - Path topLevel = Path.of("src", "test", "resources", "dfg"); - List result = - TestUtils.analyze( - List.of(topLevel.resolve("compoundoperator.cpp").toFile()), topLevel, true); - - BinaryOperator rwCompoundOperator = - TestUtils.findByUniqueName(TestUtils.subnodesOfType(result, BinaryOperator.class), "+="); - DeclaredReferenceExpression expression = - TestUtils.findByUniqueName( - TestUtils.subnodesOfType(result, DeclaredReferenceExpression.class), "i"); - VariableDeclaration variableDeclaration = - TestUtils.findByUniqueName( - TestUtils.subnodesOfType(result, VariableDeclaration.class), "i"); - - Set prevDFGOperator = rwCompoundOperator.getPrevDFG(); - Set nextDFGOperator = rwCompoundOperator.getNextDFG(); - - assertTrue(prevDFGOperator.contains(expression)); - assertTrue(nextDFGOperator.contains(expression)); - - Set prevDFGDeclaredReferenceExpression = expression.getPrevDFG(); - Set nextDFGDeclaredReferenceExpression = expression.getNextDFG(); - - assertTrue(prevDFGDeclaredReferenceExpression.contains(variableDeclaration)); - assertTrue(nextDFGDeclaredReferenceExpression.contains(variableDeclaration)); - } - - @Test - void testUnaryOperatorDFG() throws Exception { - Path topLevel = Path.of("src", "test", "resources", "dfg"); - List result = - TestUtils.analyze(List.of(topLevel.resolve("unaryoperator.cpp").toFile()), topLevel, true); - - UnaryOperator rwUnaryOperator = - TestUtils.findByUniqueName(TestUtils.subnodesOfType(result, UnaryOperator.class), "++"); - DeclaredReferenceExpression expression = - TestUtils.findByUniqueName( - TestUtils.subnodesOfType(result, DeclaredReferenceExpression.class), "i"); - VariableDeclaration variableDeclaration = - TestUtils.findByUniqueName( - TestUtils.subnodesOfType(result, VariableDeclaration.class), "i"); - - Set prevDFGOperator = rwUnaryOperator.getPrevDFG(); - Set nextDFGOperator = rwUnaryOperator.getNextDFG(); - - assertTrue(prevDFGOperator.contains(expression)); - assertTrue(nextDFGOperator.contains(expression)); - - Set prevDFGDeclaredReferenceExpression = expression.getPrevDFG(); - Set nextDFGDeclaredReferenceExpression = expression.getNextDFG(); - - assertTrue(prevDFGDeclaredReferenceExpression.contains(variableDeclaration)); - assertTrue(nextDFGDeclaredReferenceExpression.contains(variableDeclaration)); - } -} diff --git a/src/test/resources/dfg/ControlFlowSensitiveDFGIfMerge.java b/src/test/resources/dfg/ControlFlowSensitiveDFGIfMerge.java new file mode 100644 index 0000000000..3ddcf9125e --- /dev/null +++ b/src/test/resources/dfg/ControlFlowSensitiveDFGIfMerge.java @@ -0,0 +1,13 @@ +public class ControlFlowSensitiveDFGIfMerge { + void func() { + int a = 1; + if (args.length > 3) { + a = 2; + } else { + System.out.println(a); + } + + int b = a; + } + +} \ No newline at end of file diff --git a/src/test/resources/dfg/ControlFlowSensitiveDFGIfNoMerge.java b/src/test/resources/dfg/ControlFlowSensitiveDFGIfNoMerge.java new file mode 100644 index 0000000000..c0d9f24213 --- /dev/null +++ b/src/test/resources/dfg/ControlFlowSensitiveDFGIfNoMerge.java @@ -0,0 +1,11 @@ +public class ControlFlowSensitiveDFGIfNoMerge { + void func2() { + int a = 1; + if (args.length > 3) { + a = 2; + } else { + a = 4; + int b = a; + } + } +} diff --git a/src/test/resources/dfg/ControlFlowSensitiveDFGSwitch.java b/src/test/resources/dfg/ControlFlowSensitiveDFGSwitch.java new file mode 100644 index 0000000000..08c9175c4b --- /dev/null +++ b/src/test/resources/dfg/ControlFlowSensitiveDFGSwitch.java @@ -0,0 +1,22 @@ +public class ControlFlowSesitiveDFGSwitch { + void func3() { + int swithVal = 3; + int a = 0; + + switch (swithVal) { + case 1: + a = 10; + break; + case 2: + a = 11; + break; + case 3: + a = 12; // Fall through + default: + System.out.println(a); + break; + } + + int b = a; + } +}