/* Soot - a J*va Optimization Framework * Copyright (C) 2003 Jerome Miecznikowski * * This library is free software; you can redistribute 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 library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ package soot.dava.toolkits.base.misc; import soot.*; import soot.util.*; import java.util.*; import soot.jimple.*; import soot.jimple.internal.JExitMonitorStmt; import soot.jimple.toolkits.callgraph.*; /* * Nomair A. Naeem 7th April 2006 * This class detects and propagates whether the signature of a method should have some throws Exception constructs. * * The reason we need to do this is since the JVM does not force all compilers to store throws information * as attributes (javac does it ) but other compilers are not forced to do it * * Hence if we are coming from javac we dont need to perform this analysis since we already have the information * if we are not coming from javac then we need to perform this analysis to say what the checked exceptions are for this method. * * Alls good until u try to decompile code like this * try{ * synchronized(bla){ * bla * bla * } * }catch(InterruptedException e){ * bla * } * * If you create bytecode for this you will notice that because exitmointer has to be invoked if an exception occurs * this is done by catching a Throwable(all possible exceptions) exiting the monitor and rethrowing the exception. * * Now that is alright the problem occurs because InterruptedExceptions will be caught but since we are throwing the * general Throwable exception this algorithm says that the method should state in its signature that it throws * java.lang,Throwable. * CHANGE LOG: current fix is to hack into the algo find the place where we are about to add the java.lang.Throwable * and if it is near an exit monitor we know dava is going to convert this to a synch and hence not add this exception!! * * */ public class ThrowFinder { public ThrowFinder( Singletons.Global g ) {} public static ThrowFinder v() { return G.v().soot_dava_toolkits_base_misc_ThrowFinder(); } private HashSet<SootMethod> registeredMethods; private HashMap<Stmt, HashSet<SootClass>> protectionSet; public static boolean DEBUG=false; public void find() { G.v().out.print( "Verifying exception handling.. "); registeredMethods = new HashSet<SootMethod>(); protectionSet = new HashMap<Stmt, HashSet<SootClass>>(); CallGraph cg; if( Scene.v().hasCallGraph() ) { cg = Scene.v().getCallGraph(); } else { new CallGraphBuilder().build(); cg = Scene.v().getCallGraph(); Scene.v().releaseCallGraph(); } IterableSet worklist = new IterableSet(); G.v().out.print( "\b. "); G.v().out.flush(); // Get all the methods, and find protection for every statement. Iterator classIt = Scene.v().getApplicationClasses().iterator(); while (classIt.hasNext()) { Iterator methodIt = ((SootClass) classIt.next()).methodIterator(); while (methodIt.hasNext()) { SootMethod m = (SootMethod) methodIt.next(); register_AreasOfProtection( m); worklist.add( m); } } // Build the subClass and superClass mappings. HashMap<SootClass, IterableSet> subClassSet = new HashMap<SootClass, IterableSet>(), superClassSet = new HashMap<SootClass, IterableSet>(); HashSet<SootClass> applicationClasses = new HashSet<SootClass>(); applicationClasses.addAll( Scene.v().getApplicationClasses()); classIt = Scene.v().getApplicationClasses().iterator(); while (classIt.hasNext()) { SootClass c = (SootClass) classIt.next(); IterableSet superClasses = superClassSet.get( c); if (superClasses == null) { superClasses = new IterableSet(); superClassSet.put( c, superClasses); } IterableSet subClasses = subClassSet.get( c); if (subClasses == null) { subClasses = new IterableSet(); subClassSet.put( c, subClasses); } if (c.hasSuperclass()) { SootClass superClass = c.getSuperclass(); IterableSet superClassSubClasses = subClassSet.get( superClass); if (superClassSubClasses == null) { superClassSubClasses = new IterableSet(); subClassSet.put( superClass, superClassSubClasses); } superClassSubClasses.add( c); superClasses.add( superClass); } Iterator interfaceIt = c.getInterfaces().iterator(); while (interfaceIt.hasNext()) { SootClass interfaceClass = (SootClass) interfaceIt.next(); IterableSet interfaceClassSubClasses = subClassSet.get( interfaceClass); if (interfaceClassSubClasses == null) { interfaceClassSubClasses = new IterableSet(); subClassSet.put( interfaceClass, interfaceClassSubClasses); } interfaceClassSubClasses.add( c); superClasses.add( interfaceClass); } } // Build the subMethod and superMethod mappings. HashMap<SootMethod, IterableSet> agreementMethodSet = new HashMap<SootMethod, IterableSet>(); // Get exceptions from throw statements and add them to the exceptions that the method throws. Iterator worklistIt = worklist.iterator(); while (worklistIt.hasNext()) { SootMethod m = (SootMethod) worklistIt.next(); if (!m.isAbstract() && !m.isNative() ) { List<SootClass> exceptionList = m.getExceptions(); IterableSet exceptionSet = new IterableSet( exceptionList); boolean changed = false; Iterator it = m.retrieveActiveBody().getUnits().iterator(); while (it.hasNext()) { Unit u = (Unit) it.next(); HashSet handled = protectionSet.get(u); if (u instanceof ThrowStmt) { Type t = ((ThrowStmt) u).getOp().getType(); if (t instanceof RefType) { SootClass c = ((RefType) t).getSootClass(); if ((handled_Exception( handled, c) == false) && (exceptionSet.contains( c) == false)) { /* * Nomair A Naeem 7th April * HACK TRYING TO MATCH PATTERN * label0: r3 = r0; entermonitor r0; label1: r1.up(); r0.wait(); exitmonitor r3; label2: goto label6; label3: $r5 := @caughtexception; label4: r4 = $r5; exitmonitor r3; label5: throw r4; HERE IS THE THROW WE JUST DETECTED LOOK and see if the previous unit is an exitmonitor label6: goto label8; label7: $r6 := @caughtexception; r7 = $r6; label8: r1.down(); return; catch java.lang.Throwable from label1 to label2 with label3; catch java.lang.Throwable from label4 to label5 with label3; catch java.lang.InterruptedException from label0 to label6 with label7; * */ PatchingChain list = m.retrieveActiveBody().getUnits(); Unit pred = (Unit)list.getPredOf(u); if(! (pred instanceof JExitMonitorStmt)){ exceptionSet.add( c); changed = true; if(DEBUG) System.out.println("Added exception which is explicitly thrown"+c.getName()); } else{ if(DEBUG) System.out.println("Found monitor exit"+pred+" hence not adding"); } } } } } it = cg.edgesOutOf(m); while (it.hasNext()) { Edge e = (Edge) it.next(); Stmt callSite = e.srcStmt(); if( callSite == null ) continue; HashSet handled = protectionSet.get( callSite); SootMethod target = e.tgt(); Iterator<SootClass> exceptionIt = target.getExceptions().iterator(); while (exceptionIt.hasNext()) { SootClass exception = exceptionIt.next(); if ((handled_Exception( handled, exception) == false) && (exceptionSet.contains( exception) == false)) { exceptionSet.add( exception); changed = true; } } } if (changed) { exceptionList.clear(); exceptionList.addAll( exceptionSet); } } // While we're at it, put the superMethods and the subMethods in the agreementMethodSet. find_OtherMethods( m, agreementMethodSet, subClassSet, applicationClasses); find_OtherMethods( m, agreementMethodSet, superClassSet, applicationClasses); } // Perform worklist algorithm to propegate the throws information. while (worklist.isEmpty() == false) { SootMethod m = (SootMethod) worklist.getFirst(); worklist.removeFirst(); IterableSet agreementMethods = agreementMethodSet.get( m); if (agreementMethods != null) { Iterator amit = agreementMethods.iterator(); while (amit.hasNext()) { SootMethod otherMethod = (SootMethod) amit.next(); List<SootClass> otherExceptionsList = otherMethod.getExceptions(); IterableSet otherExceptionSet = new IterableSet( otherExceptionsList); boolean changed = false; Iterator<SootClass> exceptionIt = m.getExceptions().iterator(); while (exceptionIt.hasNext()) { SootClass exception = exceptionIt.next(); if (otherExceptionSet.contains( exception) == false) { otherExceptionSet.add( exception); changed = true; } } if (changed) { otherExceptionsList.clear(); otherExceptionsList.addAll( otherExceptionSet); if (worklist.contains( otherMethod) == false) worklist.addLast( otherMethod); } } } Iterator it = cg.edgesOutOf(m); while (it.hasNext()) { Edge e = (Edge) it.next(); Stmt callingSite = e.srcStmt(); if( callingSite == null ) continue; SootMethod callingMethod = e.src(); List<SootClass> exceptionList = callingMethod.getExceptions(); IterableSet exceptionSet = new IterableSet( exceptionList); HashSet handled = protectionSet.get( callingSite); boolean changed = false; Iterator<SootClass> exceptionIt = m.getExceptions().iterator(); while (exceptionIt.hasNext()) { SootClass exception = exceptionIt.next(); if ((handled_Exception( handled, exception) == false) && (exceptionSet.contains( exception) == false)) { exceptionSet.add( exception); changed = true; } } if (changed) { exceptionList.clear(); exceptionList.addAll( exceptionSet); if (worklist.contains( callingMethod) == false) worklist.addLast( callingMethod); } } } G.v().out.println(); G.v().out.flush(); } private void find_OtherMethods( SootMethod startingMethod, HashMap<SootMethod, IterableSet> methodMapping, HashMap<SootClass, IterableSet> classMapping, HashSet<SootClass> applicationClasses) { IterableSet worklist = (IterableSet) classMapping.get( startingMethod.getDeclaringClass()).clone(); HashSet<SootClass> touchSet = new HashSet<SootClass>(); touchSet.addAll( worklist); String signature = startingMethod.getSubSignature(); while (worklist.isEmpty() == false) { SootClass currentClass = (SootClass) worklist.getFirst(); worklist.removeFirst(); if (applicationClasses.contains( currentClass) == false) continue; if (currentClass.declaresMethod( signature)) { IterableSet otherMethods = methodMapping.get( startingMethod); if (otherMethods == null) { otherMethods = new IterableSet(); methodMapping.put( startingMethod, otherMethods); } otherMethods.add( currentClass.getMethod( signature)); } else { IterableSet otherClasses = classMapping.get( currentClass); if (otherClasses != null) { Iterator ocit = otherClasses.iterator(); while (ocit.hasNext()) { SootClass otherClass = (SootClass) ocit.next(); if (touchSet.contains( otherClass) == false) { worklist.addLast( otherClass); touchSet.add( otherClass); } } } } } } private void register_AreasOfProtection( SootMethod m) { if (registeredMethods.contains( m)) return; registeredMethods.add( m); if (m.hasActiveBody() == false) return; Body b = m.getActiveBody(); Chain stmts = b.getUnits(); Iterator trapIt = b.getTraps().iterator(); while (trapIt.hasNext()) { Trap t = (Trap) trapIt.next(); SootClass exception = t.getException(); Iterator sit = stmts.iterator( t.getBeginUnit(), stmts.getPredOf( t.getEndUnit())); while (sit.hasNext()) { Stmt s = (Stmt) sit.next(); HashSet<SootClass> handled = null; if ((handled = protectionSet.get( s)) == null) { handled = new HashSet<SootClass>(); protectionSet.put( s, handled); } if (handled.contains( exception) == false) handled.add( exception); } } } private boolean handled_Exception( HashSet handledExceptions, SootClass c) { SootClass thrownException = c; if (is_HandledByRuntime( thrownException)) return true; if (handledExceptions == null) return false; while (true) { if (handledExceptions.contains( thrownException)) return true; if (thrownException.hasSuperclass() == false) return false; thrownException = thrownException.getSuperclass(); } } private boolean is_HandledByRuntime( SootClass c) { SootClass thrownException = c, runtimeException = Scene.v().getSootClass( "java.lang.RuntimeException"), error = Scene.v().getSootClass( "java.lang.Error"); while (true) { if ((thrownException == runtimeException) || (thrownException == error)) return true; if (thrownException.hasSuperclass() == false) return false; thrownException = thrownException.getSuperclass(); } } }