package soot.jimple.toolkits.thread; import soot.*; import java.util.*; import soot.jimple.toolkits.infoflow.*; import soot.jimple.toolkits.thread.mhp.*; import soot.jimple.*; // ThreadLocalObjectsAnalysis written by Richard L. Halpert, 2007-03-05 // Runs LocalObjectsAnalysis for the special case where we want to know // if a reference is local to all threads from which it is reached. public class ThreadLocalObjectsAnalysis extends LocalObjectsAnalysis implements IThreadLocalObjectsAnalysis { MhpTester mhp; List<AbstractRuntimeThread> threads; InfoFlowAnalysis primitiveDfa; static boolean printDebug = false; Map valueCache; Map fieldCache; Map invokeCache; public ThreadLocalObjectsAnalysis(MhpTester mhp) // must include main class { super(new InfoFlowAnalysis(false, true, printDebug)); // ref-only, with inner fields this.mhp = mhp; this.threads = mhp.getThreads(); this.primitiveDfa = new InfoFlowAnalysis(true, true, printDebug); // ref+primitive, with inner fields valueCache = new HashMap(); fieldCache = new HashMap(); invokeCache = new HashMap(); } // Forces the majority of computation to take place immediately, rather than on-demand // might occasionally compute more than is necessary public void precompute() { for(AbstractRuntimeThread thread : threads) { for(Object item : thread.getRunMethods()) { SootMethod runMethod = (SootMethod) item; if(runMethod.getDeclaringClass().isApplicationClass()) getClassLocalObjectsAnalysis(runMethod.getDeclaringClass()); } } } // override protected ClassLocalObjectsAnalysis newClassLocalObjectsAnalysis(LocalObjectsAnalysis loa, InfoFlowAnalysis dfa, UseFinder uf, SootClass sc) { // find the right run methods to use for threads of type sc List<SootMethod> runMethods = new ArrayList<SootMethod>(); Iterator<AbstractRuntimeThread> threadsIt = threads.iterator(); while(threadsIt.hasNext()) { AbstractRuntimeThread thread = threadsIt.next(); Iterator<Object> runMethodsIt = thread.getRunMethods().iterator(); while(runMethodsIt.hasNext()) { SootMethod runMethod = (SootMethod) runMethodsIt.next(); if( runMethod.getDeclaringClass() == sc ) runMethods.add(runMethod); } } return new ClassLocalObjectsAnalysis(loa, dfa, primitiveDfa, uf, sc, runMethods); } // Determines if a RefType Local or a FieldRef is Thread-Local public boolean isObjectThreadLocal(Value localOrRef, SootMethod sm) { if(threads.size() <= 1) return true; // Pair cacheKey = new Pair(new EquivalentValue(localOrRef), sm); // if(valueCache.containsKey(cacheKey)) // { // return ((Boolean) valueCache.get(cacheKey)).booleanValue(); // } if(printDebug) G.v().out.println("- " + localOrRef + " in " + sm + " is..."); for(AbstractRuntimeThread thread : mhp.getThreads()) { for(Object meth : thread.getRunMethods()) { SootMethod runMethod = (SootMethod) meth; if( runMethod.getDeclaringClass().isApplicationClass() && !isObjectLocalToContext(localOrRef, sm, runMethod)) { if(printDebug) G.v().out.println(" THREAD-SHARED (simpledfa " + ClassInfoFlowAnalysis.methodCount + " smartdfa " + SmartMethodInfoFlowAnalysis.counter + " smartloa " + SmartMethodLocalObjectsAnalysis.counter + ")"); // valueCache.put(cacheKey, Boolean.FALSE); // escapesThrough(localOrRef, sm); return false; } } } if(printDebug) G.v().out.println(" THREAD-LOCAL (simpledfa " + ClassInfoFlowAnalysis.methodCount + " smartdfa " + SmartMethodInfoFlowAnalysis.counter + " smartloa " + SmartMethodLocalObjectsAnalysis.counter + ")");// (" + localOrRef + " in " + sm + ")"); // valueCache.put(cacheKey, Boolean.TRUE); return true; } /* public boolean isFieldThreadLocal(SootField sf, SootMethod sm) // this is kind of meaningless..., if we're looking in a particular method, we'd use isObjectThreadLocal { G.v().out.println("- Checking if " + sf + " in " + sm + " is thread-local"); Iterator threadClassesIt = threadClasses.iterator(); while(threadClassesIt.hasNext()) { SootClass threadClass = (SootClass) threadClassesIt.next(); if(!isFieldLocalToContext(sf, sm, threadClass)) { G.v().out.println(" THREAD-SHARED"); return false; } } G.v().out.println(" THREAD-LOCAL");// (" + sf + " in " + sm + ")"); return true; } */ public boolean hasNonThreadLocalEffects(SootMethod containingMethod, InvokeExpr ie) { if(threads.size() <= 1) return true; return true; /* Pair cacheKey = new Pair(new EquivalentValue(ie), containingMethod); if(invokeCache.containsKey(cacheKey)) { return ((Boolean) invokeCache.get(cacheKey)).booleanValue(); } G.v().out.print("- " + ie + " in " + containingMethod + " has "); Iterator threadsIt = threads.iterator(); while(threadsIt.hasNext()) { AbstractRuntimeThread thread = (AbstractRuntimeThread) threadsIt.next(); Iterator runMethodsIt = thread.getRunMethods().iterator(); while(runMethodsIt.hasNext()) { SootMethod runMethod = (SootMethod) runMethodsIt.next(); if( runMethod.getDeclaringClass().isApplicationClass() && hasNonLocalEffects(containingMethod, ie, runMethod)) { G.v().out.println("THREAD-VISIBLE (simpledfa " + ClassInfoFlowAnalysis.methodCount + " smartdfa " + SmartMethodInfoFlowAnalysis.counter + " smartloa " + SmartMethodLocalObjectsAnalysis.counter + ")");// (" + ie + " in " + containingMethod + ")"); invokeCache.put(cacheKey, Boolean.TRUE); return true; } } } G.v().out.println("THREAD-PRIVATE (simpledfa " + ClassInfoFlowAnalysis.methodCount + " smartdfa " + SmartMethodInfoFlowAnalysis.counter + " smartloa " + SmartMethodLocalObjectsAnalysis.counter + ")");// (" + ie + " in " + containingMethod + ")"); invokeCache.put(cacheKey, Boolean.FALSE); return false; //*/ } /** Returns a list of thread-shared sources and sinks. Returns empty list if not actually a shared value. */ public List escapesThrough(Value sharedValue, SootMethod containingMethod) { List ret = new ArrayList(); // The containingMethod might be called from multiple threads // It is possible for interestingValue to be thread-shared from some threads but not others, // so we must look at each thread separately. for(AbstractRuntimeThread thread : mhp.getThreads()) { // Each "abstract thread" from the MHP analysis actually represents a "Thread.start()" statement that could // be starting one of several different kinds of threads. We must consider each kind separately. for(Object meth : thread.getRunMethods()) { SootMethod runMethod = (SootMethod) meth; // We can only analyze application classes for TLO if( runMethod.getDeclaringClass().isApplicationClass() && !isObjectLocalToContext(sharedValue, containingMethod, runMethod)) { // This is one of the threads for which sharedValue is thread-shared // so now we will look for which object it escapes through ClassLocalObjectsAnalysis cloa = getClassLocalObjectsAnalysis(containingMethod.getDeclaringClass()); CallLocalityContext clc = cloa.getMergedContext(containingMethod); // Get the method info flow analysis object SmartMethodInfoFlowAnalysis smifa = dfa.getMethodInfoFlowAnalysis(containingMethod); // Get an IFA node for our sharedValue EquivalentValue sharedValueEqVal; if(sharedValue instanceof InstanceFieldRef) sharedValueEqVal = InfoFlowAnalysis.getNodeForFieldRef(containingMethod, ((FieldRef) sharedValue).getField()); else sharedValueEqVal = new EquivalentValue(sharedValue); // Get the sources of our interesting value List<EquivalentValue> sources = smifa.sourcesOf(sharedValueEqVal); for(EquivalentValue source : sources) { if(source.getValue() instanceof Ref) { if(clc != null && !clc.isFieldLocal(source)) // (bail out if clc is null) { ret.add(source); // System.out.println(sharedValue + " in " + containingMethod + " escapes through " + source); } } } } } } return ret; } }