All Downloads are FREE. Search and download functionalities are using the official Maven repository.

soot.jimple.infoflow.methodSummary.taintWrappers.resolvers.SummaryResolver Maven / Gradle / Ivy

package soot.jimple.infoflow.methodSummary.taintWrappers.resolvers;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

import heros.solver.IDESolver;
import soot.Hierarchy;
import soot.Scene;
import soot.SootClass;
import soot.SootMethod;
import soot.jimple.infoflow.methodSummary.data.provider.IMethodSummaryProvider;
import soot.jimple.infoflow.methodSummary.data.summary.ClassMethodSummaries;
import soot.jimple.infoflow.methodSummary.data.summary.ClassSummaries;
import soot.jimple.infoflow.util.ByReferenceBoolean;

/**
 * A resolver for finding all applicable summaries for a given method call
 * 
 * @author Steven Arzt
 *
 */
public class SummaryResolver {

	private static final int MAX_HIERARCHY_DEPTH = 10;

	protected final LoadingCache methodToImplFlows = IDESolver.DEFAULT_CACHE_BUILDER
			.build(new CacheLoader() {
				@Override
				public SummaryResponse load(SummaryQuery query) throws Exception {
					final SootClass calleeClass = query.calleeClass;
					final SootClass declaredClass = query.declaredClass;
					final String methodSig = query.subsignature;
					final ClassSummaries classSummaries = new ClassSummaries();
					boolean directHit = false;
					ByReferenceBoolean isClassSupported = new ByReferenceBoolean(false);

					// Get the flows in the target method
					if (calleeClass != null)
						directHit = getSummaries(methodSig, classSummaries, calleeClass, isClassSupported);

					// If we haven't found any summaries, we look at the class from the declared
					// type at the call site
					if (declaredClass != null && !directHit)
						directHit = getSummaries(methodSig, classSummaries, declaredClass, isClassSupported);

					// If we still don't have anything, we must try the hierarchy. Since this
					// best-effort approach can be fairly imprecise, it is our last resort.
					if (!directHit && calleeClass != null)
						directHit = getSummariesHierarchy(methodSig, classSummaries, calleeClass, isClassSupported);
					if (declaredClass != null && !directHit)
						directHit = getSummariesHierarchy(methodSig, classSummaries, declaredClass, isClassSupported);

					if (directHit && !classSummaries.isEmpty())
						return new SummaryResponse(classSummaries, true);
					else if (directHit || isClassSupported.value)
						return SummaryResponse.EMPTY_BUT_SUPPORTED;
					else
						return SummaryResponse.NOT_SUPPORTED;
				}

				private void updateClassExclusive(ByReferenceBoolean classSupported, SootClass sc, String subsig) {
					if (classSupported.value)
						return;

					if (sc.getMethodUnsafe(subsig) == null)
						return;

					ClassMethodSummaries cms = flows.getClassFlows(sc.getName());
					classSupported.value |= cms != null && cms.isExclusiveForClass();
				}

				/**
				 * Checks whether we have summaries for the given method signature in the given
				 * class
				 * 
				 * @param methodSig The subsignature of the method for which to get summaries
				 * @param summaries The summary object to which to add the discovered summaries
				 * @param clazz     The class for which to look for summaries
				 * @return True if summaries were found, false otherwise
				 */
				private boolean getSummaries(final String methodSig, final ClassSummaries summaries, SootClass clazz,
						ByReferenceBoolean classSupported) {
					// Do we have direct support for the target class?
					if (summaries.merge(flows.getMethodFlows(clazz, methodSig)))
						return true;

					// Do we support any interface this class might have?
					if (checkInterfaces(methodSig, summaries, clazz, classSupported))
						return true;

					updateClassExclusive(classSupported, clazz, methodSig);

					SootMethod targetMethod = clazz.getMethodUnsafe(methodSig);
					// If the target is abstract and we haven't found any flows,
					// we check for child classes
					if (!clazz.isConcrete() || targetMethod == null || !targetMethod.isConcrete()) {
						for (SootClass parentClass : getAllParentClasses(clazz)) {
							// Do we have support for the target class?
							if (summaries.merge(flows.getMethodFlows(parentClass, methodSig)))
								return true;

							// Do we support any interface this class might have?
							if (checkInterfaces(methodSig, summaries, parentClass, classSupported))
								return true;

							updateClassExclusive(classSupported, parentClass, methodSig);
						}
					}

					return false;
				}

				/**
				 * Checks whether we have summaries for the given method signature in a class in
				 * the hierarchy of the given class
				 * 
				 * @param methodSig The subsignature of the method for which to get summaries
				 * @param summaries The summary object to which to add the discovered summaries
				 * @param clazz     The class for which to look for summaries
				 * @return True if summaries were found, false otherwise
				 */
				private boolean getSummariesHierarchy(final String methodSig, final ClassSummaries summaries,
						SootClass clazz, ByReferenceBoolean classSupported) {
					// Don't try to look up the whole Java hierarchy
					if (clazz.getName().equals("java.lang.Object"))
						return false;

					// If the target is abstract and we haven't found any flows,
					// we check for child classes. Since the summaries are class-specific and we
					// don't really know which child class we're looking for, we have to merge the
					// flows for all possible classes.
					SootMethod targetMethod = clazz.getMethodUnsafe(methodSig);
					if (!clazz.isConcrete() || targetMethod == null || !targetMethod.isConcrete()) {
						int found = 0;
						Set childClasses = getAllChildClasses(clazz);
						for (SootClass childClass : childClasses) {
							// Do we have support for the target class?
							if (summaries.merge(flows.getMethodFlows(childClass, methodSig)))
								found++;

							// Do we support any interface this class might have?
							if (checkInterfaces(methodSig, summaries, childClass, classSupported))
								found++;

							updateClassExclusive(classSupported, childClass, methodSig);

							// If we have too many summaries that could be applicable, we abort here to
							// avoid false positives
							if (found > MAX_HIERARCHY_DEPTH)
								return false;
						}
						return found > 0;
					}
					return false;
				}

				/**
				 * Checks whether we have summaries for the given method signature in the given
				 * interface or any of its super-interfaces
				 * 
				 * @param methodSig The subsignature of the method for which to get summaries
				 * @param summaries The summary object to which to add the discovered summaries
				 * @param clazz     The interface for which to look for summaries
				 * @return True if summaries were found, false otherwise
				 */
				private boolean checkInterfaces(String methodSig, ClassSummaries summaries, SootClass clazz,
						ByReferenceBoolean classSupported) {
					for (SootClass intf : clazz.getInterfaces()) {
						// Directly check the interface
						if (summaries.merge(flows.getMethodFlows(intf, methodSig)))
							return true;

						for (SootClass parent : getAllParentClasses(intf)) {
							// Do we have support for the interface?
							if (summaries.merge(flows.getMethodFlows(parent, methodSig)))
								return true;

							updateClassExclusive(classSupported, parent, methodSig);
						}
					}

					// We inject the hierarchy from summaries before the data flow analysis, thus
					// the soot hierarchy already contains the manual information provided in the
					// xmls.
					return false;
				}

			});

	protected final IMethodSummaryProvider flows;
	private final Hierarchy hierarchy;

	/**
	 * Creates a new instance of the {@link SummaryResolver} class
	 * 
	 * @param flows The provider that supplies the data flow summaries
	 */
	public SummaryResolver(IMethodSummaryProvider flows) {
		this.flows = flows;
		this.hierarchy = Scene.v().getActiveHierarchy();
	}

	/**
	 * Gets all child classes of the given class. If the given class is an
	 * interface, all implementors of this interface and its all of child-
	 * interfaces are returned.
	 * 
	 * @param sc The class or interface for which to get the children
	 * @return The children of the given class or interface
	 */
	private Set getAllChildClasses(SootClass sc) {
		List workList = new ArrayList();
		workList.add(sc);

		Set doneSet = new HashSet();
		Set classes = new HashSet<>();

		while (!workList.isEmpty()) {
			SootClass curClass = workList.remove(0);
			if (!doneSet.add(curClass))
				continue;

			if (curClass.isInterface()) {
				List hierarchyImplementers = hierarchy.getImplementersOf(curClass);
				workList.addAll(hierarchyImplementers);

				List subinterfaces = hierarchy.getSubinterfacesOf(curClass);
				workList.addAll(subinterfaces);
			} else {
				List hierarchyClasses = hierarchy.getSubclassesOf(curClass);
				workList.addAll(hierarchyClasses);
				classes.add(curClass);
			}
		}

		return classes;
	}

	/**
	 * Gets all parent classes of the given class. If the given class is an
	 * interface, all parent implementors of this interface are returned.
	 * 
	 * @param sc The class or interface for which to get the parents
	 * @return The parents of the given class or interface
	 */
	private Set getAllParentClasses(SootClass sc) {
		List workList = new ArrayList();
		workList.add(sc);

		Set doneSet = new HashSet();
		Set classes = new HashSet<>();

		while (!workList.isEmpty()) {
			SootClass curClass = workList.remove(0);
			if (!doneSet.add(curClass))
				continue;

			if (curClass.isInterface()) {
				List hierarchyClasses = hierarchy.getSuperinterfacesOf(curClass);
				workList.addAll(hierarchyClasses);
				classes.add(curClass);
			} else {
				List hierarchyClasses = hierarchy.getSuperclassesOf(curClass);
				workList.addAll(hierarchyClasses);
				classes.add(curClass);
			}
		}

		return classes;
	}

	/**
	 * Resolves the given query for a data flow summary
	 * 
	 * @param query The query that defines the data flow summary to obtain
	 * @return The data flow summaries that match the given query
	 */
	public SummaryResponse resolve(SummaryQuery query) {
		return methodToImplFlows.getUnchecked(query);
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy