2015-06-14 07:04:57 +00:00
/ *
2018-10-30 00:09:20 +00:00
* Copyright ( c ) 2010 - 2015 SAP SE .
* 2016 - 2018 The University of Sheffield .
*
2015-06-14 07:04:57 +00:00
* All rights reserved . This program and the accompanying materials
2018-10-30 00:09:20 +00:00
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2 . 0
* which is available at https : //www.eclipse.org/legal/epl-2.0/
2015-06-14 07:04:57 +00:00
*
2018-10-30 00:09:20 +00:00
* SPDX - License - Identifier : EPL - 2 . 0
2015-06-14 07:04:57 +00:00
* /
2017-05-21 14:58:16 +00:00
package com.logicalhacking.dasca.dataflow.util ;
2015-06-14 07:04:57 +00:00
import java.io.BufferedWriter ;
import java.io.File ;
import java.io.FileWriter ;
import java.io.IOException ;
import java.util.ArrayList ;
import java.util.Collections ;
import java.util.HashMap ;
import java.util.HashSet ;
import java.util.Iterator ;
import java.util.List ;
import org.apache.log4j.Logger ;
import com.ibm.wala.dataflow.IFDS.ICFGSupergraph ;
import com.ibm.wala.ipa.cfg.BasicBlockInContext ;
import com.ibm.wala.ssa.IR ;
import com.ibm.wala.ssa.SSABinaryOpInstruction ;
import com.ibm.wala.ssa.SSAConditionalBranchInstruction ;
import com.ibm.wala.ssa.SSAGotoInstruction ;
import com.ibm.wala.ssa.SSAInstruction ;
import com.ibm.wala.ssa.SSAInvokeInstruction ;
import com.ibm.wala.ssa.SymbolTable ;
import com.ibm.wala.ssa.analysis.IExplodedBasicBlock ;
import com.ibm.wala.types.MethodReference ;
import cvc3.Expr ;
import cvc3.SatResult ;
import cvc3.ValidityChecker ;
/ * *
* static class for SG helper methods
*
* /
public class SuperGraphUtil {
private static final Logger log = AnalysisUtil . getLogger ( SuperGraphUtil . class ) ;
/ * *
* Adds every nodes ID to iDs , iff it is on a path between the entry and exit point . Uses recursive DFS
* @param iDs
* @param sg
* @param sgNodes
* @param sgNodesReverse
* @param currentId
* @param endId
* /
private static void relevantPathSearch (
HashSet < Integer > iDs ,
ICFGSupergraph sg ,
HashMap < Integer , BasicBlockInContext < IExplodedBasicBlock > > sgNodes ,
HashMap < BasicBlockInContext < IExplodedBasicBlock > , Integer > sgNodesReverse ,
ArrayList < MethodReference > acceptedMethods ,
int currentId ,
int endId ) {
if ( iDs . contains ( currentId ) ) {
return ;
}
iDs . add ( currentId ) ;
if ( currentId = = endId ) {
return ;
}
BasicBlockInContext < IExplodedBasicBlock > bbic = sgNodes . get ( currentId ) ;
boolean isInvoke = bbic . getLastInstruction ( ) instanceof SSAInvokeInstruction ;
boolean isStringConcat = isInvoke & & bbic . getLastInstruction ( ) . toString ( ) . contains ( " StringBuilder " ) & & ( bbic . getLastInstruction ( ) . toString ( ) . contains ( " append " ) | bbic . getLastInstruction ( ) . toString ( ) . contains ( " toString " ) ) ;
Iterator < BasicBlockInContext < IExplodedBasicBlock > > sucIt = sg . getSuccNodes ( bbic ) ;
while ( sucIt . hasNext ( ) ) {
BasicBlockInContext < IExplodedBasicBlock > nextChild = sucIt . next ( ) ;
if ( isStringConcat ) {
if ( ! sucIt . hasNext ( ) ) { // last association of a String concatenation is java intern
continue ;
}
}
MethodReference method = nextChild . getMethod ( ) . getReference ( ) ;
if ( isInvoke ) {
if ( ! acceptedMethods . contains ( method ) ) {
acceptedMethods . add ( method ) ;
}
} else {
if ( ! acceptedMethods . contains ( method ) ) {
2016-10-30 16:12:19 +00:00
log . debug ( " supergraph cut at ' " + bbic . getNumber ( ) + " -> " + nextChild . getNumber ( ) + " ( "
+ nextChild . toString ( ) + " )' " ) ;
2015-06-14 07:04:57 +00:00
continue ;
}
}
IExplodedBasicBlock del = nextChild . getDelegate ( ) ;
if ( del . isEntryBlock ( ) & & del . toString ( ) . contains ( " init " ) ) {
log . debug ( " supergraph cut at ' " + bbic . getNumber ( ) + " -> " + nextChild . getNumber ( ) + " ( " + nextChild . toString ( ) + " )' " ) ;
continue ;
}
relevantPathSearch ( iDs , sg , sgNodes , sgNodesReverse , acceptedMethods , sgNodesReverse . get ( nextChild ) , endId ) ;
}
}
/ * *
* Analyzes the supergraph and saves the relevant part as a dot file to the location & lt ; graphs folder & gt ; / < entryClass /
* @return
* /
public static int analyzeAndSaveSuperGraph ( ICFGSupergraph sg , String entryClass , String entryMethod ) {
int weaknessCount = 0 ;
if ( sg = = null ) {
throw new IllegalArgumentException ( " sg was null for entry Class \" " + entryClass + " \" and entry method \" " + entryMethod + " \" " ) ;
}
2016-08-23 13:47:25 +00:00
log . info ( " --- start analyzing " + entryClass + " . " + entryMethod + " --- " ) ;
2015-06-14 07:04:57 +00:00
ValidityChecker vc = ValidityChecker . create ( ) ;
vc . push ( ) ;
Iterator < BasicBlockInContext < IExplodedBasicBlock > > completeIterator = sg . iterator ( ) ;
int analysisLevel = AnalysisUtil . getPropertyInteger ( AnalysisUtil . CONFIG_ANALYSIS_DEPTH ) ;
HashMap < Integer , BasicBlockInContext < IExplodedBasicBlock > > sgNodes = new HashMap < Integer , BasicBlockInContext < IExplodedBasicBlock > > ( ) ;
HashMap < BasicBlockInContext < IExplodedBasicBlock > , Integer > sgNodesReversed = new HashMap < BasicBlockInContext < IExplodedBasicBlock > , Integer > ( ) ;
2016-09-18 01:00:09 +00:00
HashMap < SSAInstructionKey , Integer > sgNodesInstId = new HashMap < SSAInstructionKey , Integer > ( ) ;
2015-06-14 07:04:57 +00:00
ArrayList < MethodReference > acceptedMethods = new ArrayList < MethodReference > ( ) ;
// Add all blocks into a Map with a unique identifier in both directions and find entry/exit node
int i = 0 ;
int mainEntryId = 0 ;
int mainExitId = 0 ;
while ( completeIterator . hasNext ( ) ) {
BasicBlockInContext < IExplodedBasicBlock > current = completeIterator . next ( ) ;
sgNodes . put ( i , current ) ;
2016-09-18 00:04:01 +00:00
log . debug ( " sgNodes: insert node " + i + " ==> " + current ) ;
2015-06-14 07:04:57 +00:00
sgNodesReversed . put ( current , i ) ;
Iterator < SSAInstruction > instIt = current . iterator ( ) ;
while ( instIt . hasNext ( ) ) {
SSAInstruction inst = instIt . next ( ) ;
2016-09-18 01:00:09 +00:00
sgNodesInstId . put ( new SSAInstructionKey ( inst ) , i ) ;
2016-09-18 00:04:01 +00:00
log . debug ( " sgNodesInstId: insert node " + i + " ==> " + inst ) ;
2015-06-14 07:04:57 +00:00
/ * add to include other required methods into CFG
if ( inst instanceof AstJavaInvokeInstruction ) {
AstJavaInvokeInstruction in = ( AstJavaInvokeInstruction ) inst ;
MethodReference meth = in . getDeclaredTarget ( ) ;
if ( meth . toString ( ) . contains ( " calledMethod " ) ) { // check for required method
acceptedMethods . add ( meth ) ;
}
}
//*/
}
String signature = current . getMethod ( ) . getSignature ( ) ;
// find entry and exit nodes
if ( signature . contains ( entryClass ) & & signature . contains ( entryMethod ) & & current . isEntryBlock ( ) ) { // FIXME: entry/exit nodes definition via name is too weak
mainEntryId = i ;
2016-09-18 00:04:01 +00:00
log . error ( " Found Entry Block " + i + " ( " + entryClass + " / " + entryMethod + " : " ) ;
log . error ( " " + signature ) ;
2015-06-14 07:04:57 +00:00
} else if ( signature . contains ( entryClass ) & & signature . contains ( entryMethod ) & & current . isExitBlock ( ) ) {
mainExitId = i ;
2016-09-18 00:04:01 +00:00
log . error ( " Found Exit Block " + i + " ( " + entryClass + " / " + entryMethod + " : " ) ;
log . error ( " " + signature ) ;
2015-06-14 07:04:57 +00:00
}
i + + ;
}
if ( mainEntryId = = 0 & & mainExitId = = 0 ) {
2016-10-30 16:12:19 +00:00
log . error ( " " + entryClass + " . " + entryMethod +
" : empty entry method, ensure invocation in main method " +
" (mainEntryId = " + mainEntryId + " / mainExitId = " + mainExitId + " ) " ) ;
2016-08-27 13:27:20 +00:00
return - 1 ;
2015-06-14 07:04:57 +00:00
}
HashSet < Integer > relevantIDs = new HashSet < Integer > ( ) ;
BasicBlockInContext < IExplodedBasicBlock > bbic = sgNodes . get ( mainEntryId ) ;
acceptedMethods . add ( bbic . getMethod ( ) . getReference ( ) ) ;
2016-10-30 16:12:19 +00:00
log . error ( " " + entryClass + " . " + entryMethod +
2016-08-23 13:47:25 +00:00
" start recursive graph building " ) ;
2015-06-14 07:04:57 +00:00
relevantPathSearch ( relevantIDs , sg , sgNodes , sgNodesReversed , acceptedMethods , mainEntryId , mainExitId ) ;
// remove irrelevant nodes (not on at least one path between entry and exit node)
log . debug ( " remove irrelevant nodes " ) ;
for ( int j = 0 ; j < i ; j + + ) {
BasicBlockInContext < IExplodedBasicBlock > tmp = sgNodes . get ( j ) ;
if ( ! relevantIDs . contains ( j ) ) {
sgNodesReversed . remove ( tmp ) ;
sgNodes . remove ( j ) ;
2016-09-18 00:04:01 +00:00
log . debug ( " removing node: " + j ) ;
2015-06-14 07:04:57 +00:00
}
}
// build separate adjacency list
2016-10-30 16:12:19 +00:00
log . debug ( " " + entryClass + " . " + entryMethod +
2016-08-23 13:47:25 +00:00
" build seperate adjacency list " ) ;
2015-06-14 07:04:57 +00:00
HashMap < Integer , ArrayList < Integer > > adjList = new HashMap < Integer , ArrayList < Integer > > ( ) ;
HashMap < Integer , ArrayList < Integer > > adjListReverse = new HashMap < Integer , ArrayList < Integer > > ( ) ;
boolean removeEmptyNodes = AnalysisUtil . getPropertyBoolean ( AnalysisUtil . CONFIG_DOT_REMOVE_EMPTY_NODES ) ;
ArrayList < Integer > emptyNodes = new ArrayList < Integer > ( ) ;
buildAdjacencyLists ( sg , sgNodes , sgNodesReversed , adjList , adjListReverse , emptyNodes , relevantIDs ) ;
2016-08-23 23:01:56 +00:00
// <<< print original adjacency list to log
2016-10-30 16:12:19 +00:00
log . debug ( " adjacency list before removing empty nodes: " ) ;
AnalysisUtil . printAdjList ( adjList , log ) ;
2016-08-23 23:01:56 +00:00
//
2015-06-14 07:04:57 +00:00
if ( removeEmptyNodes ) {
removeEmptyNodes ( emptyNodes , adjList , adjListReverse , sgNodes , sgNodesReversed ) ;
2016-08-24 23:16:05 +00:00
// <<< print adjacency list to log
2016-10-30 16:12:19 +00:00
log . debug ( " adjacency after removing empty nodes: " ) ;
AnalysisUtil . printAdjList ( adjList , log ) ;
2016-08-24 23:16:05 +00:00
//
2015-06-14 07:04:57 +00:00
}
2016-10-30 16:12:19 +00:00
log . debug ( " " + entryClass + " . " + entryMethod +
2016-08-23 13:47:25 +00:00
" add conditions to graph nodes " ) ;
2015-06-14 07:04:57 +00:00
ArrayList < Integer > visited = new ArrayList < Integer > ( ) ;
ArrayList < Integer > currentConditions = new ArrayList < Integer > ( ) ;
HashMap < Integer , Integer > currentConditionsEndId = new HashMap < Integer , Integer > ( ) ;
HashMap < Integer , ArrayList < Integer > > finalConditions = new HashMap < Integer , ArrayList < Integer > > ( ) ;
HashMap < Integer , Integer > loops = new HashMap < Integer , Integer > ( ) ;
addConditionsToGraph ( sgNodes , adjList , mainEntryId , visited , mainExitId , currentConditions , currentConditionsEndId , finalConditions , loops ) ;
// remove goto statements for later analysis steps
for ( int loopId : loops . keySet ( ) ) {
int gotoId = loops . get ( loopId ) ;
int gotoTargetId = adjList . get ( gotoId ) . get ( 0 ) ; // goto has exact one child
int afterLoopId = 0 ;
for ( int id : adjList . get ( loopId ) ) {
afterLoopId = Math . max ( afterLoopId , id ) ;
}
ArrayList < Integer > newList = new ArrayList < Integer > ( ) ;
newList . add ( afterLoopId ) ;
adjList . put ( gotoId , newList ) ;
adjListReverse . get ( afterLoopId ) . add ( gotoId ) ;
ArrayList < Integer > gotoTargetReverseList = adjListReverse . get ( gotoTargetId ) ;
ArrayList < Integer > newGotoTargetReverseList = new ArrayList < Integer > ( ) ;
for ( int child : gotoTargetReverseList ) {
if ( child ! = gotoId ) {
newGotoTargetReverseList . add ( child ) ;
}
}
adjListReverse . put ( gotoTargetId , newGotoTargetReverseList ) ;
}
/ * < < < print required conditions for each node
for ( Integer key : finalConditions . keySet ( ) ) {
StringBuffer sb = new StringBuffer ( ) ;
for ( int condition : finalConditions . get ( key ) ) {
sb . append ( " , " + condition ) ;
} sb . append ( " " ) ;
log . debug ( key + " : " + sb . substring ( 2 ) ) ;
}
//*/
/ * < < < print changed adjacency list to log
log . debug ( " adjacency list after removing empty nodes: " ) ;
AnalysisUtil . printAdjList ( adjList , log ) ;
//*/
//* <<< get definition instruction for each SSA value
HashMap < Integer , SSAInstruction > definitions = AnalysisUtil . getDefs ( sgNodes ) ;
//*/
//* <<< get source code line number for each instruction
2016-09-18 01:06:08 +00:00
HashMap < SSAInstructionKey , Integer > lineNumbers = AnalysisUtil . getLineNumbers ( sgNodes ) ;
2015-06-14 07:04:57 +00:00
//*/
//* <<< get the corresponding instruction for each condition inside the callgraph
ArrayList < SSAInstruction > conditionsList = AnalysisUtil . getConditions ( sgNodes ) ;
ArrayList < Integer > conditionsIdList = new ArrayList < Integer > ( ) ;
for ( SSAInstruction cond : conditionsList ) {
2016-09-18 01:00:09 +00:00
conditionsIdList . add ( sgNodesInstId . get ( new SSAInstructionKey ( cond ) ) ) ;
2015-06-14 07:04:57 +00:00
}
//*/
//* <<< get the corresponding source code line number for each condition (node id) inside the callgraph
HashMap < Integer , Integer > conditionLineNumber = new HashMap < Integer , Integer > ( ) ;
for ( SSAInstruction instCondition : conditionsList ) {
2016-09-18 01:00:09 +00:00
int nodeId = sgNodesInstId . get ( new SSAInstructionKey ( instCondition ) ) ;
2016-09-18 01:06:08 +00:00
int lineNumber = lineNumbers . get ( new SSAInstructionKey ( instCondition ) ) ;
2015-06-14 07:04:57 +00:00
conditionLineNumber . put ( nodeId , lineNumber ) ;
}
//*/
//* <<< build CVC3 expressions from conditional instruction
log . debug ( " ** get CVC3 expressions for each conditional branch instruction " ) ;
HashMap < Integer , Expr > expressions = new HashMap < Integer , Expr > ( ) ;
IR entryIR = sgNodes . values ( ) . iterator ( ) . next ( ) . getNode ( ) . getIR ( ) ;
vc . pop ( ) ;
vc . push ( ) ;
for ( SSAInstruction instCondition : conditionsList ) {
2016-09-18 01:00:09 +00:00
int condId = sgNodesInstId . get ( new SSAInstructionKey ( instCondition ) ) ;
2015-06-14 07:04:57 +00:00
if ( loops . keySet ( ) . contains ( condId ) ) {
Expr expr = SMTChecker . getExprForLoop ( vc , instCondition , entryIR ) ;
2016-09-18 01:00:09 +00:00
expressions . put ( sgNodesInstId . get ( new SSAInstructionKey ( instCondition ) ) , expr ) ;
2015-06-14 07:04:57 +00:00
} else {
Expr expr = SMTChecker . getExprForConditionalBranchInstruction ( vc , instCondition , entryIR ) ;
2016-09-18 01:00:09 +00:00
expressions . put ( sgNodesInstId . get ( new SSAInstructionKey ( instCondition ) ) , expr ) ;
2015-06-14 07:04:57 +00:00
}
vc . pop ( ) ;
vc . push ( ) ;
}
vc . pop ( ) ;
vc . push ( ) ;
//*/
ArrayList < SSAInstruction > sqlExecutes = AnalysisUtil . getSQLExecutes ( sgNodes ) ;
log . debug ( " ** get source/sink pairs for each SQL instruction " ) ;
String sanitizerMethods = AnalysisUtil . getPropertyString ( AnalysisUtil . CONFIG_SANITIZER ) ; // good sources are specified as sanitizer
String [ ] methods = sanitizerMethods . split ( " , " ) ;
HashSet < String > sanitizer = new HashSet < String > ( ) ;
for ( int k = 0 ; k < methods . length ; k + + ) {
String sanitizerMethod = methods [ k ] . trim ( ) ;
sanitizer . add ( sanitizerMethod ) ;
}
String badSourceMethods = AnalysisUtil . getPropertyString ( AnalysisUtil . CONFIG_BAD_SRC ) ;
methods = badSourceMethods . split ( " , " ) ;
HashSet < String > badMethods = new HashSet < String > ( ) ;
for ( int k = 0 ; k < methods . length ; k + + ) {
String badMethod = methods [ k ] . trim ( ) + " () " ;
badMethods . add ( badMethod ) ;
}
//* <<< get possible vulnerabilities for each SQL execute
2016-09-18 01:12:56 +00:00
HashMap < SSAInstructionKey , ArrayList < SSAInstruction > > sinkSources = new HashMap < SSAInstructionKey , ArrayList < SSAInstruction > > ( ) ;
2015-06-14 07:04:57 +00:00
for ( SSAInstruction ssaInstruction : sqlExecutes ) {
boolean isPreparedStmt = ssaInstruction . toString ( ) . contains ( " Prepared " ) ;
2016-09-18 01:12:56 +00:00
sinkSources . put ( new SSAInstructionKey ( ssaInstruction ) , AnalysisUtil . analyzeStatementExecute ( ssaInstruction , definitions , isPreparedStmt , badMethods ) ) ;
2015-06-14 07:04:57 +00:00
}
boolean containsVulnerability = false ;
for ( SSAInstruction sink : sqlExecutes ) {
2016-09-18 01:12:56 +00:00
if ( ! sinkSources . containsKey ( new SSAInstructionKey ( sink ) ) ) { // no vulnerability possible
2015-06-14 07:04:57 +00:00
continue ;
}
2016-09-18 01:12:56 +00:00
ArrayList < SSAInstruction > badSources = sinkSources . get ( new SSAInstructionKey ( sink ) ) ;
2015-06-14 07:04:57 +00:00
if ( badSources ! = null ) {
for ( SSAInstruction source : badSources ) {
boolean isNotMutuallyExclusive = true ;
if ( analysisLevel > = AnalysisUtil . ANALYSIS_DEPTH_EXCLUSIVE ) {
isNotMutuallyExclusive = isNotMutuallyExclusive ( sink , source , sgNodesInstId , finalConditions , expressions , vc ) ;
}
if ( isNotMutuallyExclusive ) {
boolean isNotSanitized = true ;
if ( analysisLevel > = AnalysisUtil . ANALYSIS_DEPTH_SANITIZING ) {
2016-09-18 00:04:01 +00:00
log . debug ( " Calling isNotSanitized: " ) ;
2016-09-18 01:00:09 +00:00
log . debug ( " Source ( " + sgNodesInstId . get ( new SSAInstructionKey ( source ) ) + " ): " + source ) ;
log . debug ( " Sink ( " + sgNodesInstId . get ( new SSAInstructionKey ( sink ) ) + " ): " + sink ) ;
isNotSanitized = isNotSanitized ( sgNodesInstId . get ( new SSAInstructionKey ( source ) ) , sgNodesInstId . get ( new SSAInstructionKey ( sink ) ) , adjList , finalConditions , expressions , vc , conditionsIdList , sanitizer , sgNodes ) ;
2015-06-14 07:04:57 +00:00
}
if ( isNotSanitized ) {
weaknessCount + + ;
containsVulnerability = true ;
2016-09-18 01:06:08 +00:00
log . warn ( " SQL execute [ " + lineNumbers . get ( new SSAInstructionKey ( sink ) ) + " ] with bad source readLine [ " + lineNumbers . get ( new SSAInstructionKey ( source ) ) + " ] ( " + entryClass + " . " + entryMethod + " ) " ) ;
2015-06-14 07:04:57 +00:00
}
}
}
}
}
if ( ! containsVulnerability ) {
log . info ( " ( " + entryClass + " . " + entryMethod + " ) is save " ) ;
}
//*/
String filePath = String . format ( " %s_SG.dot " , entryMethod ) ;
generateDotFile ( sgNodes , adjList , entryClass , filePath , finalConditions ) ;
return weaknessCount ;
}
/ * *
* Computes all possible control flows between source and sink and checks , if every single path is sanitized .
* @return true , iff there exists at least one direct unsanitized path between source and sink
* /
private static boolean isNotSanitized ( int sourceId ,
int sinkId , HashMap < Integer , ArrayList < Integer > > adjList ,
HashMap < Integer , ArrayList < Integer > > finalConditions ,
HashMap < Integer , Expr > expressions ,
ValidityChecker vc ,
ArrayList < Integer > conditionsIdList ,
HashSet < String > sanitizer ,
HashMap < Integer , BasicBlockInContext < IExplodedBasicBlock > > sgNodes ) {
log . debug ( " ** get possible control flows from source to sink " ) ;
HashMap < Integer , HashSet < ArrayList < Integer > > > paths = new HashMap < Integer , HashSet < ArrayList < Integer > > > ( ) ;
calculatePaths ( sourceId , sinkId , adjList , paths ) ;
HashSet < ArrayList < Integer > > pathList = paths . get ( sourceId ) ;
HashSet < ArrayList < Integer > > possibleFlowPaths = new HashSet < ArrayList < Integer > > ( ) ;
for ( ArrayList < Integer > path : pathList ) {
boolean isPossibleFlow = isPossibleFlow ( path , finalConditions , expressions , vc , conditionsIdList ) ;
StringBuffer sb = new StringBuffer ( ) ;
for ( int i = 0 ; i < path . size ( ) ; i + + ) {
sb . append ( " -> " + path . get ( i ) ) ;
}
sb . append ( " " ) ;
if ( isPossibleFlow ) {
log . debug ( " FLOW [possible]: " + sb . substring ( 3 ) ) ; // cut off the last arrow
possibleFlowPaths . add ( path ) ;
} else {
log . debug ( " FLOW [impossible]: " + sb . substring ( 3 ) ) ;
}
}
loopPossiblePaths :
for ( ArrayList < Integer > possibleFlowPath : possibleFlowPaths ) {
boolean containsSanitizer = false ;
for ( String sanitizerMethod : sanitizer ) {
for ( int nodeId : possibleFlowPath ) {
BasicBlockInContext < IExplodedBasicBlock > node = sgNodes . get ( nodeId ) ;
SSAInstruction inst = node . getLastInstruction ( ) ;
if ( inst ! = null ) {
if ( inst . toString ( ) . contains ( sanitizerMethod ) ) {
containsSanitizer = true ;
continue loopPossiblePaths ;
}
}
}
}
if ( ! containsSanitizer ) {
return true ; // path contains no sanitizer => is direct path between bad source and bad sink
}
}
return false ; // every possible path contained at least one sanitizer
}
private static boolean isPossibleFlow ( ArrayList < Integer > path ,
HashMap < Integer , ArrayList < Integer > > finalConditions ,
HashMap < Integer , Expr > expressions ,
ValidityChecker vc ,
ArrayList < Integer > conditionsIdList ) {
vc . pop ( ) ;
vc . push ( ) ;
List < Expr > allExpr = new ArrayList < Expr > ( ) ;
ArrayList < Integer > allConditions = new ArrayList < Integer > ( ) ;
for ( int i = 0 ; i < path . size ( ) ; i + + ) {
int node = path . get ( i ) ;
ArrayList < Integer > conditions = finalConditions . get ( node ) ;
if ( conditionsIdList . contains ( node ) ) {
ArrayList < Integer > childConditions = finalConditions . get ( path . get ( i + 1 ) . intValue ( ) ) ;
if ( childConditions = = null | | childConditions . isEmpty ( ) | | ! ( childConditions . contains ( node ) | childConditions . contains ( - node ) ) ) { // jump over if-block without else part => else condition needs to be set
allExpr . add ( vc . notExpr ( expressions . get ( node ) ) ) ;
}
}
if ( conditions ! = null ) {
for ( int condId : conditions ) {
if ( ! allConditions . contains ( condId ) ) {
allConditions . add ( condId ) ;
if ( condId < 0 ) {
allExpr . add ( vc . notExpr ( expressions . get ( Math . abs ( condId ) ) ) ) ;
} else {
allExpr . add ( expressions . get ( condId ) ) ;
}
}
}
}
}
if ( allExpr . isEmpty ( ) ) {
return true ;
}
Expr completeExpr = vc . andExpr ( allExpr ) ;
SatResult satResult = vc . checkUnsat ( completeExpr ) ;
boolean satisfiable = satResult . equals ( SatResult . SATISFIABLE ) ;
2016-09-18 00:04:01 +00:00
log . info ( " isPossibleFlow: checking expression [ " + ( satisfiable ? " SAT " : " UNSAT " ) + " ]: " + completeExpr ) ;
2015-06-14 07:04:57 +00:00
return satisfiable ;
}
private static void calculatePaths ( int currentId , int target ,
HashMap < Integer , ArrayList < Integer > > adjList ,
HashMap < Integer , HashSet < ArrayList < Integer > > > paths ) {
2016-10-30 16:12:19 +00:00
log . debug ( " searching for " + currentId + " in adjList. " ) ;
2015-06-14 07:04:57 +00:00
ArrayList < Integer > children = adjList . get ( currentId ) ;
2016-08-28 00:59:37 +00:00
if ( null = = children ) {
2016-10-30 16:12:19 +00:00
throw new RuntimeException ( " No entry found in adjList for " + currentId ) ;
2016-08-28 00:59:37 +00:00
}
2015-06-14 07:04:57 +00:00
HashSet < ArrayList < Integer > > currentPaths = new HashSet < ArrayList < Integer > > ( ) ;
for ( int child : children ) {
if ( child = = target ) {
ArrayList < Integer > last = new ArrayList < Integer > ( ) ;
last . add ( child ) ;
currentPaths . add ( last ) ;
continue ;
}
calculatePaths ( child , target , adjList , paths ) ;
HashSet < ArrayList < Integer > > childPaths = paths . get ( child ) ;
if ( childPaths ! = null ) {
for ( ArrayList < Integer > childPath : childPaths ) {
ArrayList < Integer > newPath = new ArrayList < Integer > ( ) ;
newPath . add ( child ) ;
newPath . addAll ( childPath ) ;
currentPaths . add ( newPath ) ;
}
}
}
paths . put ( currentId , currentPaths ) ;
}
/ * *
* Checks , if sink and source node are mutually excluded because of the respective conditions
* @param sink
* @param source
* @param sgNodesInstId
* @param finalConditions
* @param expressions
* @param vc
* @return
* /
private static boolean isNotMutuallyExclusive ( SSAInstruction sink ,
SSAInstruction source ,
2016-09-18 01:00:09 +00:00
HashMap < SSAInstructionKey , Integer > sgNodesInstId ,
2015-06-14 07:04:57 +00:00
HashMap < Integer , ArrayList < Integer > > finalConditions ,
HashMap < Integer , Expr > expressions , ValidityChecker vc ) {
vc . pop ( ) ;
vc . push ( ) ;
2016-09-18 01:00:09 +00:00
int sourceId = sgNodesInstId . get ( new SSAInstructionKey ( source ) ) ;
int sinkId = sgNodesInstId . get ( new SSAInstructionKey ( sink ) ) ;
2015-06-14 07:04:57 +00:00
ArrayList < Integer > combinedConditions = new ArrayList < Integer > ( ) ;
if ( finalConditions . containsKey ( sourceId ) ) {
combinedConditions . addAll ( finalConditions . get ( sourceId ) ) ;
}
if ( finalConditions . containsKey ( sinkId ) ) {
combinedConditions . addAll ( finalConditions . get ( sinkId ) ) ;
}
Expr expr = getConditionExpression ( combinedConditions , expressions , vc ) ;
vc . pop ( ) ;
vc . push ( ) ;
if ( expr . toString ( ) . equalsIgnoreCase ( " null " ) ) {
2016-10-30 16:12:19 +00:00
log . debug ( " isNotMutuallyExclusive: EXPR: " + expr + " is NULL (combinedConditions: "
+ combinedConditions + " / expressions: " + expressions + " ) " ) ;
2015-06-14 07:04:57 +00:00
return true ;
}
SatResult satResult = vc . checkUnsat ( expr ) ;
boolean satisfiable = satResult . equals ( SatResult . SATISFIABLE ) ;
2016-09-18 00:04:01 +00:00
log . info ( " isNotMutuallyExclusive: checking expression [ " + ( satisfiable ? " SAT " : " UNSAT " ) + " ]: " + expr ) ;
2015-06-14 07:04:57 +00:00
return satisfiable ;
}
private static Expr getConditionExpression ( ArrayList < Integer > nodeIds , HashMap < Integer , Expr > expressions , ValidityChecker vc ) {
if ( nodeIds . isEmpty ( ) ) {
return vc . nullExpr ( ) ;
}
List < Expr > exprList = new ArrayList < Expr > ( ) ;
for ( int i = 0 ; i < nodeIds . size ( ) ; i + + ) {
Integer conditionId = nodeIds . get ( i ) ;
Expr tmpExpr = expressions . get ( Math . abs ( conditionId ) ) ;
2016-09-18 00:04:01 +00:00
log . debug ( " expressions[ " + conditionId + " ] = " + tmpExpr ) ;
2015-06-14 07:04:57 +00:00
if ( conditionId < 0 ) {
tmpExpr = vc . notExpr ( tmpExpr ) ;
}
exprList . add ( tmpExpr ) ;
}
return vc . andExpr ( exprList ) ;
}
private static void removeEmptyNodes ( ArrayList < Integer > emptyNodes ,
HashMap < Integer , ArrayList < Integer > > adjList ,
HashMap < Integer , ArrayList < Integer > > adjListReverse , HashMap < Integer , BasicBlockInContext < IExplodedBasicBlock > > sgNodes , HashMap < BasicBlockInContext < IExplodedBasicBlock > , Integer > sgNodesReversed ) {
Collections . sort ( emptyNodes ) ;
StringBuffer sb = new StringBuffer ( ) ;
for ( int integer : emptyNodes ) {
sb . append ( " , " + integer ) ;
}
sb . append ( " " ) ;
log . debug ( " empty nodes in supergraph: " + sb . substring ( 1 ) ) ;
// remove empty nodes from supergraph
log . debug ( " remove empty nodes from supergraph " ) ;
for ( Integer kill : emptyNodes ) {
ArrayList < Integer > children = adjList . get ( kill ) ;
if ( children = = null ) {
children = new ArrayList < Integer > ( ) ;
}
// insert new edges around removed nodes
for ( Integer father : adjListReverse . get ( kill ) ) {
ArrayList < Integer > tmpList = adjList . get ( father ) ;
if ( tmpList = = null ) {
tmpList = new ArrayList < Integer > ( ) ;
}
tmpList . remove ( kill ) ;
tmpList . addAll ( children ) ;
for ( Integer childId : children ) {
ArrayList < Integer > childFathers = adjListReverse . get ( childId ) ;
if ( childFathers = = null ) {
childFathers = new ArrayList < Integer > ( ) ;
}
childFathers . remove ( kill ) ;
childFathers . add ( father ) ;
adjListReverse . put ( childId , childFathers ) ;
}
adjList . put ( father , tmpList ) ;
}
}
for ( Integer kill : emptyNodes ) { // requires two loops to build the new edges correct
adjList . remove ( kill ) ;
sgNodesReversed . remove ( sgNodes . get ( kill ) ) ;
sgNodes . remove ( kill ) ;
}
}
private static void buildAdjacencyLists (
ICFGSupergraph sg , HashMap < Integer , BasicBlockInContext < IExplodedBasicBlock > > sgNodes ,
HashMap < BasicBlockInContext < IExplodedBasicBlock > , Integer > sgNodesReversed ,
HashMap < Integer , ArrayList < Integer > > adjList ,
HashMap < Integer , ArrayList < Integer > > adjListReverse ,
ArrayList < Integer > emptyNodes , HashSet < Integer > relevantIDs ) {
for ( int key1 : sgNodes . keySet ( ) ) {
BasicBlockInContext < IExplodedBasicBlock > val1 = sgNodes . get ( key1 ) ;
Iterator < BasicBlockInContext < IExplodedBasicBlock > > sucIt = sg . getSuccNodes ( val1 ) ;
// add 'key1->sucId' to adjList
ArrayList < Integer > list = adjList . get ( key1 ) ;
if ( list = = null ) {
list = new ArrayList < Integer > ( ) ;
}
while ( sucIt . hasNext ( ) ) {
BasicBlockInContext < IExplodedBasicBlock > suc = sucIt . next ( ) ;
if ( ! sgNodesReversed . containsKey ( suc ) ) {
continue ;
}
int sucId = sgNodesReversed . get ( suc ) ;
if ( ! list . contains ( sucId ) ) {
list . add ( sucId ) ;
}
// add 'sucId->key1' to adjListReverse
ArrayList < Integer > listReverse = adjListReverse . get ( sucId ) ;
if ( listReverse = = null ) {
listReverse = new ArrayList < Integer > ( ) ;
}
if ( ! listReverse . contains ( key1 ) ) {
listReverse . add ( key1 ) ;
}
adjListReverse . put ( sucId , listReverse ) ;
// add empty nodes to list
Iterator < SSAInstruction > it = suc . iterator ( ) ;
if ( relevantIDs . contains ( sucId ) ) {
if ( ! emptyNodes . contains ( sucId ) ) {
if ( ! it . hasNext ( ) & & ! suc . isEntryBlock ( ) & & ! suc . isExitBlock ( ) & & ! suc . isCatchBlock ( ) ) {
emptyNodes . add ( sucId ) ;
}
}
}
}
adjList . put ( key1 , list ) ;
}
}
/ * *
* Analyzes the given nodes of the supergraph and fills the list finalConditions
* @param sgNodes
* @param adjList
* @param currentId
* @param visited
* @param mainEndId
* @param currentConditions
* @param currentConditionsEndId
* @param finalConditions
* @param loops
* @return
* /
private static boolean addConditionsToGraph ( // boolean return is used in recursion to signalize the existence of an else block
HashMap < Integer , BasicBlockInContext < IExplodedBasicBlock > > sgNodes ,
HashMap < Integer , ArrayList < Integer > > adjList ,
Integer currentId ,
ArrayList < Integer > visited ,
Integer mainEndId ,
ArrayList < Integer > currentConditions ,
HashMap < Integer , Integer > currentConditionsEndId ,
HashMap < Integer , ArrayList < Integer > > finalConditions ,
HashMap < Integer , Integer > loops ) {
if ( ! visited . contains ( currentId ) ) {
visited . add ( currentId ) ;
}
if ( currentId = = mainEndId ) {
finalConditions . put ( mainEndId , new ArrayList < Integer > ( ) ) ;
return false ;
}
SSAInstruction currentInstruction = sgNodes . get ( currentId ) . getLastInstruction ( ) ;
ArrayList < Integer > children = adjList . get ( currentId ) ;
Integer ifStart = 0 ;
Integer ifEnd = 0 ;
if ( ! currentConditions . isEmpty ( ) ) { // inside an if-block
ifStart = currentConditions . get ( currentConditions . size ( ) - 1 ) ; // last condition is the most inner condition
ifEnd = currentConditionsEndId . get ( ifStart ) ;
if ( currentId > Math . abs ( ifEnd ) & & sgNodes . get ( currentId ) . isCatchBlock ( ) ) { // most inner if block has else AND current node is join
return false ;
}
if ( currentId > Math . abs ( ifEnd ) & & Math . abs ( ifEnd ) ! = mainEndId ) { // most inner if block has else AND current node is join
currentConditionsEndId . put ( ifStart , currentId ) ;
return true ;
}
if ( currentId . intValue ( ) = = ifEnd . intValue ( ) ) { // end of single if block
return false ;
}
if ( currentId = = ( - ifEnd ) ) { // end of else block
ArrayList < Integer > tmpCond = new ArrayList < Integer > ( currentConditions ) ;
HashMap < Integer , Integer > tmpCondEnd = new HashMap < Integer , Integer > ( currentConditionsEndId ) ;
int lastId = currentConditions . size ( ) ;
while ( lastId > 0 & & currentId = = - tmpCondEnd . get ( tmpCond . get ( - - lastId ) ) ) { // delete all conditions, which end at this node
Integer del = currentConditions . get ( lastId ) ;
currentConditions . remove ( del ) ;
currentConditionsEndId . remove ( del ) ;
}
}
} // END IF inside an if-block
if ( currentInstruction instanceof SSAConditionalBranchInstruction ) {
Integer childTrue = Math . min ( children . get ( 0 ) , children . get ( 1 ) ) ; // WALA always takes the true branch first
Integer childFalse = Math . max ( children . get ( 0 ) , children . get ( 1 ) ) ;
ArrayList < Integer > conditions = new ArrayList < Integer > ( ) ;
for ( int i = 0 ; i < currentConditions . size ( ) ; i + + ) {
conditions . add ( currentConditions . get ( i ) ) ;
}
finalConditions . put ( currentId , conditions ) ;
currentConditions . add ( currentId ) ;
boolean isLastIfBlock = childTrue . intValue ( ) = = mainEndId . intValue ( ) ; // => its an if block without else and without any instructions after it
if ( isLastIfBlock ) {
childTrue = Math . max ( children . get ( 0 ) , children . get ( 1 ) ) ;
currentConditionsEndId . put ( currentId , mainEndId ) ;
} else {
currentConditionsEndId . put ( currentId , childFalse ) ;
}
boolean isRealIfElse = addConditionsToGraph ( sgNodes , adjList , childTrue , visited , mainEndId , currentConditions , currentConditionsEndId , finalConditions , loops ) ;
currentConditions . remove ( currentId ) ;
Integer newEnd = currentConditionsEndId . remove ( currentId ) ;
if ( isRealIfElse ) {
currentConditions . add ( - currentId ) ;
currentConditionsEndId . put ( - currentId , - newEnd ) ; // last visited is the reached join from if-block
}
if ( ! isLastIfBlock ) {
return addConditionsToGraph ( sgNodes , adjList , childFalse , visited , mainEndId , currentConditions , currentConditionsEndId , finalConditions , loops ) ;
}
return isRealIfElse ;
} // END IF conditional branch instruction
else if ( currentInstruction instanceof SSAGotoInstruction ) {
int child = children . get ( 0 ) ; // goto always points to exactly one child node
if ( currentId = = Math . abs ( ifEnd ) - 1 ) { // last node of if block
ArrayList < Integer > conditions = new ArrayList < Integer > ( ) ;
for ( int i = 0 ; i < currentConditions . size ( ) ; i + + ) {
conditions . add ( currentConditions . get ( i ) ) ;
}
finalConditions . put ( currentId , conditions ) ;
if ( ! ( Math . abs ( ifEnd ) = = child ) ) {
if ( child < = Math . abs ( ifStart ) ) { // is loop
loops . put ( ifStart , currentId . intValue ( ) ) ;
return false ;
}
currentConditionsEndId . put ( ifStart , child ) ;
return true ;
} else {
return false ;
}
}
} // END IF goto instruction
else {
ArrayList < Integer > conditions = new ArrayList < Integer > ( ) ;
for ( int i = 0 ; i < currentConditions . size ( ) ; i + + ) {
conditions . add ( currentConditions . get ( i ) ) ;
}
finalConditions . put ( currentId , conditions ) ;
}
2016-09-18 00:04:56 +00:00
2015-06-14 07:04:57 +00:00
System . out . println ( " " + currentInstruction ) ;
boolean isRealIfElse = false ;
for ( int i = 0 ; i < children . size ( ) ; i + + ) {
int child = children . get ( i ) ;
isRealIfElse = isRealIfElse | addConditionsToGraph ( sgNodes , adjList , child , visited , mainEndId , currentConditions , currentConditionsEndId , finalConditions , loops ) ;
}
return isRealIfElse ;
}
private static void generateDotFile (
HashMap < Integer , BasicBlockInContext < IExplodedBasicBlock > > sgNodes ,
HashMap < Integer , ArrayList < Integer > > adjList , String entryClass ,
String filePath , HashMap < Integer , ArrayList < Integer > > finalConditions ) {
FileWriter fstream ;
try {
log . debug ( " start generating dot file " ) ;
String path = AnalysisUtil . getPropertyString ( AnalysisUtil . CONFIG_DOT_PATH ) + File . separator ;
path + = " java " + File . separator ;
File sgDir = new File ( path + entryClass ) ;
sgDir . mkdirs ( ) ;
File sgFile = new File ( path + entryClass + File . separator + filePath ) ;
fstream = new FileWriter ( sgFile ) ;
BufferedWriter out = new BufferedWriter ( fstream ) ;
out . write ( " digraph SuperGraph { " ) ;
out . newLine ( ) ;
out . write ( " node [shape=record]; " ) ;
out . newLine ( ) ;
// add relevant nodes to dot
log . debug ( " add nodes to dot file " ) ;
for ( Integer key : sgNodes . keySet ( ) ) {
BasicBlockInContext < IExplodedBasicBlock > val = sgNodes . get ( key ) ;
Iterator < SSAInstruction > insIt = val . iterator ( ) ;
IExplodedBasicBlock del = val . getDelegate ( ) ;
int key2 = del . getGraphNodeId ( ) ; //cfg.getNumber(val);
// print the labels and define the id (key)
out . write ( key + " [ " ) ;
out . write ( " label = \" <f0> " + key + " - " + key2 + " | <f1> " + AnalysisUtil . sanitize ( val . getMethod ( ) . getSignature ( ) ) ) ;
IR ir = val . getNode ( ) . getIR ( ) ;
SymbolTable symTab = ir . getSymbolTable ( ) ;
int j = 2 ;
// print the instruction field of the label
while ( insIt . hasNext ( ) ) {
// insCount++;
SSAInstruction ins = insIt . next ( ) ;
if ( ins instanceof SSAConditionalBranchInstruction ) {
ins = ( SSAConditionalBranchInstruction ) ins ;
SSAInstruction cmp = val . getNode ( ) . getDU ( ) . getDef ( ins . getUse ( 0 ) ) ;
if ( cmp instanceof SSABinaryOpInstruction ) {
SSABinaryOpInstruction bCmp = ( SSABinaryOpInstruction ) cmp ;
out . write ( " | <f " + j + + + " > " + bCmp . toString ( symTab ) ) ;
}
} else if ( ins instanceof SSAInvokeInstruction ) {
//TODO
}
out . write ( " | <f " + j + + + " > " + AnalysisUtil . sanitize ( ins . toString ( symTab ) ) ) ;
}
if ( finalConditions . containsKey ( key ) ) {
StringBuffer sb = new StringBuffer ( ) ;
for ( int condition : finalConditions . get ( key ) ) {
sb . append ( " , " + condition ) ;
}
sb . append ( " " ) ;
out . write ( " | <f " + j + + + " > " + sb . substring ( 2 ) ) ;
}
if ( val . isEntryBlock ( ) ) {
out . write ( " | <f " + j + + + " > [ENTRY] " ) ;
} else if ( val . isExitBlock ( ) ) {
out . write ( " | <f " + j + + + " > [EXIT] " ) ;
} else if ( val . isCatchBlock ( ) ) {
out . write ( " | <f " + j + + + " > [CATCH] " ) ;
}
out . write ( " \" ]; " ) ;
out . newLine ( ) ;
}
// add relevant edges to dot
log . debug ( " add edges to dot file " ) ;
for ( Integer src : adjList . keySet ( ) ) {
for ( int dest : adjList . get ( src ) ) {
out . write ( src + " -> " + dest + " ; " ) ;
out . newLine ( ) ;
}
}
// write dot file to file system
out . write ( " } " ) ;
out . close ( ) ;
log . info ( " dot file generated ( " + sgFile . getAbsolutePath ( ) + " ) " ) ;
} catch ( IOException e ) {
e . printStackTrace ( ) ;
}
}
}